Skip to content

Commit 7be0525

Browse files
authored
Initial agentic-components package + storybook (#4134)
* check in component library with storybook * get all repo validation tasks passing * finish storybook setup * remove initial PLAN.md file used for standup
1 parent 059fca0 commit 7be0525

29 files changed

Lines changed: 2670 additions & 20 deletions

knip.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const config = async (): Promise<KnipConfig> => {
2727
const baseProject = knip.project ?? ['src/**/*'];
2828
const project = knip.extraProject ? [...baseProject, ...knip.extraProject] : baseProject;
2929

30-
const ignoreDependencies: string[] = [];
30+
const ignoreDependencies: string[] = knip.ignoreDependencies ?? [];
3131

3232
return [
3333
relativePath,

lage.config.mjs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const config = {
2020
// which doesn't exist, so lage silently drops the edge. Point at the
2121
// specific root target with the packageName#task syntax so every
2222
// package's tests wait for the whole-repo build to finish.
23-
dependsOn: ['@fluentui-react-native/root#build'],
23+
dependsOn: ['@fluentui-react-native/root#root-build'],
2424
inputs: [],
2525
outputs: [],
2626
},
@@ -44,10 +44,25 @@ const config = {
4444
'lint-repo': {
4545
cache: false,
4646
},
47+
prebuild: {
48+
/**
49+
* Prebuild is a pre-build step that can either modify the source code or generate additional files in the project.
50+
*/
51+
dependsOn: ['^prebuild'],
52+
inputs: ['**/*', '!node_modules/**/*', '!dist/**/*', '!lib/**/*', '!lib-commonjs/**/*'],
53+
outputs: ['lib/**/*', 'src/**/*'],
54+
},
55+
'root-prebuild': {
56+
cache: false,
57+
},
58+
'root-build': {
59+
dependsOn: ['@fluentui-react-native/root#root-prebuild'],
60+
cache: false,
61+
},
4762

4863
// ── Pipeline aliases ───────────────────────────────────────────────────
4964
'repo-checks': ['lint-repo', 'check-publishing'],
50-
buildci: ['lint-repo', 'check-publishing', 'build', 'test', 'lint'],
65+
buildci: ['lint-repo', 'check-publishing', '@fluentui-react-native/root#root-build', 'test', 'lint'],
5166

5267
// ── Worker tasks ───────────────────────────────────────────────────────
5368
'test-links': {

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
"scripts"
1616
],
1717
"scripts": {
18-
"build": "tsgo -b",
19-
"clean-all": "node ./scripts/src/preinstall/clean-all.ts",
18+
"build": "lage root-build",
2019
"docs": "yarn workspace fluent-rn-website start",
2120
"bundle:repo": "lage bundle",
22-
"clean": "lage clean",
21+
"clean": "tsgo -b --clean && lage clean",
22+
"clean-all": "node ./scripts/src/preinstall/clean-all.ts",
2323
"change": "node .github/scripts/change.mts",
2424
"changeset:version": "node .github/scripts/changeset-version-with-postbump.mts",
2525
"change:check": "node .github/scripts/change.mts --check",
2626
"check-publishing": "node ./scripts/src/cli.ts check-publishing",
2727
"lint-fix": "cross-env FURN_FIX_MODE=true lage lint",
28+
"root-build": "tsgo -b",
29+
"root-prebuild": "lage prebuild",
2830
"preinstall": "node ./scripts/src/preinstall/use-yarn-please.ts",
2931
"format": "oxfmt",
3032
"format:check": "oxfmt --check",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@fluentui-react-native/scripts/jest-config');
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"name": "@fluentui-react-native/agentic-components",
3+
"version": "0.1.0",
4+
"private": true,
5+
"description": "A library of agent-generated FluentUI React Native components",
6+
"license": "MIT",
7+
"author": "",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/microsoft/fluentui-react-native.git",
11+
"directory": "packages/agentic-components"
12+
},
13+
"type": "module",
14+
"main": "lib/index.js",
15+
"module": "lib/index.js",
16+
"types": "lib/index.d.ts",
17+
"exports": {
18+
".": {
19+
"types": "./lib/index.d.ts",
20+
"react-native": "./src/index.ts",
21+
"import": "./lib/index.js",
22+
"default": "./src/index.ts"
23+
}
24+
},
25+
"scripts": {
26+
"build": "tsgo -b",
27+
"clean": "fluentui-scripts clean",
28+
"depcheck": "fluentui-scripts depcheck",
29+
"format": "fluentui-scripts format",
30+
"lint": "fluentui-scripts lint",
31+
"test": "fluentui-scripts jest",
32+
"update-snapshots": "fluentui-scripts jest -u"
33+
},
34+
"devDependencies": {
35+
"@babel/core": "catalog:",
36+
"@fluentui-react-native/scripts": "workspace:*",
37+
"@react-native-community/cli": "^20.0.0",
38+
"@react-native-community/cli-platform-android": "^20.0.0",
39+
"@react-native-community/cli-platform-ios": "^20.0.0",
40+
"@react-native/babel-preset": "^0.81.0",
41+
"@react-native/metro-config": "^0.81.0",
42+
"@types/react": "~19.1.4",
43+
"@types/react-test-renderer": "^19.1.0",
44+
"react": "19.1.4",
45+
"react-native": "^0.81.6",
46+
"react-native-macos": "^0.81.0",
47+
"react-test-renderer": "19.1.4"
48+
},
49+
"peerDependencies": {
50+
"@types/react": "~18.2.0 || ~19.0.0 || ~19.1.4",
51+
"react": "18.2.0 || 19.0.0 || 19.1.4",
52+
"react-native": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.6",
53+
"react-native-macos": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0"
54+
},
55+
"peerDependenciesMeta": {
56+
"@types/react": {
57+
"optional": true
58+
},
59+
"react-native-macos": {
60+
"optional": true
61+
}
62+
},
63+
"furn": {
64+
"jestPlatform": "macos"
65+
},
66+
"rnx-kit": {
67+
"kitType": "library",
68+
"alignDeps": {
69+
"capabilities": [
70+
"core",
71+
"core-macos",
72+
"react-test-renderer"
73+
]
74+
},
75+
"extends": "@fluentui-react-native/scripts/kit-config"
76+
}
77+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Button component tests renders in a disabled state 1`] = `
4+
<View
5+
acceptsFirstMouse={false}
6+
accessibilityRole="button"
7+
accessibilityState={
8+
{
9+
"busy": undefined,
10+
"checked": undefined,
11+
"disabled": true,
12+
"expanded": undefined,
13+
"selected": undefined,
14+
}
15+
}
16+
accessibilityValue={
17+
{
18+
"max": undefined,
19+
"min": undefined,
20+
"now": undefined,
21+
"text": undefined,
22+
}
23+
}
24+
accessible={true}
25+
collapsable={false}
26+
enableFocusRing={false}
27+
focusable={false}
28+
keyDownEvents={
29+
[
30+
{
31+
"key": " ",
32+
},
33+
{
34+
"key": "Enter",
35+
},
36+
]
37+
}
38+
mouseDownCanMoveWindow={false}
39+
onBlur={[Function]}
40+
onClick={[Function]}
41+
onFocus={[Function]}
42+
onKeyDown={[Function]}
43+
onKeyUp={[Function]}
44+
onMouseEnter={[Function]}
45+
onMouseLeave={[Function]}
46+
onResponderGrant={[Function]}
47+
onResponderMove={[Function]}
48+
onResponderRelease={[Function]}
49+
onResponderTerminate={[Function]}
50+
onResponderTerminationRequest={[Function]}
51+
onStartShouldSetResponder={[Function]}
52+
style={
53+
[
54+
{
55+
"alignItems": "center",
56+
"backgroundColor": "#0078d4",
57+
"borderRadius": 4,
58+
"justifyContent": "center",
59+
"paddingHorizontal": 16,
60+
"paddingVertical": 8,
61+
},
62+
false,
63+
{
64+
"backgroundColor": "#c8c8c8",
65+
},
66+
undefined,
67+
]
68+
}
69+
>
70+
<Text
71+
style={
72+
[
73+
{
74+
"color": "#ffffff",
75+
"fontSize": 14,
76+
"fontWeight": "600",
77+
},
78+
{
79+
"color": "#888888",
80+
},
81+
]
82+
}
83+
>
84+
Disabled
85+
</Text>
86+
</View>
87+
`;
88+
89+
exports[`Button component tests renders with a title 1`] = `
90+
<View
91+
acceptsFirstMouse={true}
92+
accessibilityRole="button"
93+
accessibilityState={
94+
{
95+
"busy": undefined,
96+
"checked": undefined,
97+
"disabled": false,
98+
"expanded": undefined,
99+
"selected": undefined,
100+
}
101+
}
102+
accessibilityValue={
103+
{
104+
"max": undefined,
105+
"min": undefined,
106+
"now": undefined,
107+
"text": undefined,
108+
}
109+
}
110+
accessible={true}
111+
collapsable={false}
112+
enableFocusRing={true}
113+
focusable={true}
114+
keyDownEvents={
115+
[
116+
{
117+
"key": " ",
118+
},
119+
{
120+
"key": "Enter",
121+
},
122+
]
123+
}
124+
mouseDownCanMoveWindow={false}
125+
onBlur={[Function]}
126+
onClick={[Function]}
127+
onFocus={[Function]}
128+
onKeyDown={[Function]}
129+
onKeyUp={[Function]}
130+
onMouseEnter={[Function]}
131+
onMouseLeave={[Function]}
132+
onResponderGrant={[Function]}
133+
onResponderMove={[Function]}
134+
onResponderRelease={[Function]}
135+
onResponderTerminate={[Function]}
136+
onResponderTerminationRequest={[Function]}
137+
onStartShouldSetResponder={[Function]}
138+
style={
139+
[
140+
{
141+
"alignItems": "center",
142+
"backgroundColor": "#0078d4",
143+
"borderRadius": 4,
144+
"justifyContent": "center",
145+
"paddingHorizontal": 16,
146+
"paddingVertical": 8,
147+
},
148+
false,
149+
undefined,
150+
undefined,
151+
]
152+
}
153+
>
154+
<Text
155+
style={
156+
[
157+
{
158+
"color": "#ffffff",
159+
"fontSize": 14,
160+
"fontWeight": "600",
161+
},
162+
undefined,
163+
]
164+
}
165+
>
166+
Click me
167+
</Text>
168+
</View>
169+
`;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Meta, StoryObj } from '@storybook/react-native';
2+
import { Button } from './button';
3+
4+
/**
5+
* Storybook stories for the {@link Button} component. These are loaded by the storybook app in
6+
* `packages/agentic-components/storybook` via its `.storybook/main` stories glob.
7+
*/
8+
const meta: Meta<typeof Button> = {
9+
title: 'Components/Button',
10+
component: Button,
11+
args: {
12+
title: 'Button',
13+
disabled: false,
14+
},
15+
};
16+
17+
export default meta;
18+
19+
type Story = StoryObj<typeof Button>;
20+
21+
export const Default: Story = {};
22+
23+
export const Disabled: Story = {
24+
args: {
25+
title: 'Disabled',
26+
disabled: true,
27+
},
28+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from 'react';
2+
import * as renderer from 'react-test-renderer';
3+
import { Button } from './button';
4+
5+
describe('Button component tests', () => {
6+
it('renders with a title', () => {
7+
let component: renderer.ReactTestRenderer;
8+
renderer.act(() => {
9+
component = renderer.create(React.createElement(Button, { title: 'Click me' }));
10+
});
11+
expect(component!.toJSON()).toMatchSnapshot();
12+
});
13+
14+
it('renders in a disabled state', () => {
15+
let component: renderer.ReactTestRenderer;
16+
renderer.act(() => {
17+
component = renderer.create(React.createElement(Button, { title: 'Disabled', disabled: true }));
18+
});
19+
expect(component!.toJSON()).toMatchSnapshot();
20+
});
21+
22+
it('fires onPress when pressed', () => {
23+
const onPress = jest.fn();
24+
let component: renderer.ReactTestRenderer;
25+
renderer.act(() => {
26+
component = renderer.create(React.createElement(Button, { title: 'Press', onPress }));
27+
});
28+
const pressable = component!.root.findByProps({ accessibilityRole: 'button' });
29+
renderer.act(() => {
30+
pressable.props.onPress({});
31+
});
32+
expect(onPress).toHaveBeenCalledTimes(1);
33+
});
34+
});

0 commit comments

Comments
 (0)