-
Notifications
You must be signed in to change notification settings - Fork 36
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
base: main
Are you sure you want to change the base?
Changes from all commits
acbe1c1
c955230
4070b6d
76a1d47
b717d2c
f9245c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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", | ||
"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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
> | ||
<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 /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()], | ||
}) |
There was a problem hiding this comment.
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