Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Introduce react-fluentui-examples project #189

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"installDependencies": true,
"startCommand": "npm run dev"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Fluent UI React V9 - List - Active Element

This example shows how a List component can be used to visualize an active element.

You can use selection and custom styles to display the active element in a specific way. This is useful for scenarios where you want to show the details of the selected item, for example.

In this example, we are also demonstrating how the `onFocus` prop can be utilized to change the selected item immediately upon receiving focus. This allows us to show the details of the selected item in the right panel as user navigates through the list with the keyboard.

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/microsoft/fluentui-contrib/tree/jirivyhnalek/react-fluentui-examples/packages/react-fluentui-examples/List/list-active-element?file=src%2FList-Active-Element.tsx)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
rel="shortcut icon"
type="image/x-icon"
href="https://github.com/microsoft/fluentui-contrib/tree/main/packages/react-fluentui-examples/public/favicon-192.ico"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Fluent UI V9 List Active Element</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "react-fluentui-examples-list-active-element",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no build infra in the contrib repro to handle application type projects, as all the infra is built around publishable packages. The task runner (NX) isn't even integrated to this package and it maintains its own dependencies.

I'd suggest to move it out to another /apps folder on the root to keep the separation from the publishable packages

"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@fluentui/react-components": "^9.54.2",
"@fluentui/react-list-preview": "^0.2.9",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"typescript": "^5.2.2",
"vite": "^5.3.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
Button,
makeStyles,
Persona,
mergeClasses,
Text,
tokens,
} from '@fluentui/react-components';
import { Mic16Regular } from '@fluentui/react-icons';
import { List, ListItem } from '@fluentui/react-list-preview';

import * as React from 'react';

type Item = {
name: string;
id: string;
avatar: string;
};

const items: Item[] = [
'Melda Bevel',
'Demetra Manwaring',
'Eusebia Stufflebeam',
'Israel Rabin',
'Bart Merrill',
'Sonya Farner',
'Kristan Cable',
].map((name) => ({
name,
id: name,
avatar:
'https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-react-assets/persona-male.png',
}));

const useStyles = makeStyles({
selectedInfo: {
marginTop: '16px',
},
buttonWrapper: {
alignSelf: 'center',
},
item: {
cursor: 'pointer',
padding: '2px 6px',
justifyContent: 'space-between',
border: '1px solid transparent',
},
itemActive: {
backgroundColor: tokens.colorBrandBackground2,
border: `1px solid ${tokens.colorBrandStroke1}`,
borderRadius: '4px',
},
});

export const ListActiveElement = () => {
const classes = useStyles();

const [activeItem, setActiveItem] = React.useState<string | number | null>(
null
);

const onFocus = React.useCallback((event) => {
// Ignore bubbled up events from the children
if (event.target !== event.currentTarget) {
return;
}
setActiveItem(event.target.dataset.value);
}, []);

const onAction = React.useCallback((_, { value }) => {
setActiveItem(value);
}, []);

return (
<div>
<List navigationMode="composite">
{items.map(({ name, avatar }) => (
<ListItem
key={name}
value={name}
className={mergeClasses(
classes.item,
activeItem === name && classes.itemActive

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there also be aria-selected or some other aria prop indicating the selection state?

)}
onAction={onAction}
data-value={name}
aria-label={name}
onFocus={onFocus}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please test this with JAWS browser mode as that doesn't send focus events as you navigate.
Also, try with at least windows touch screen device if you are still able to change the value with just tap

>
<Persona
name={name}
role="gridcell"
secondaryText="Available"
presence={{ status: 'available' }}
avatar={{
image: {
src: avatar,
},
}}
/>
<div role="gridcell" className={classes.buttonWrapper}>
<Button
aria-label={`Mute ${name}`}
size="small"
icon={<Mic16Regular />}
onClick={(e) => {
e.stopPropagation();
alert(`Muting ${name}`);
}}
/>
</div>
</ListItem>
))}
</List>
<div className={classes.selectedInfo}>
Currently selected:{' '}
<Text block weight="bold">
{activeItem}
</Text>
</div>
</div>
);
};

export default ListActiveElement;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { FluentProvider, webLightTheme } from '@fluentui/react-components';

import Example from './List-Active-Element.tsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<FluentProvider theme={webLightTheme}>
<Example />
Copy link
Member

@ling1726 ling1726 Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some kind of mounting/routing support - Otherwise there's too much barrier for someone to contribute the 2nd,3rd

</FluentProvider>
</React.StrictMode>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "List/src/index.tsx", "List/src/App.tsx", "List/src/main.tsx"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
Loading
Loading