@lifesg/react-design-system 4.0.0-alpha.13 → 4.0.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/external/dompurify/dist/purify.es.js +4 -2
- package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
- package/cjs/modal-v2/slots/slot-styles.js +1 -1
- package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
- package/cjs/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
- package/external/dompurify/dist/purify.es.js +4 -2
- package/external/dompurify/dist/purify.es.js.map +1 -1
- package/modal-v2/slots/slot-styles.js +1 -1
- package/modal-v2/slots/slot-styles.js.map +1 -1
- package/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
- package/notification-banner/notification-banner-hoc.js +1 -1
- package/notification-banner/notification-banner-hoc.js.map +1 -1
- package/package.json +2 -2
- package/scripts/cli.js +18 -0
- package/skills/fds-build/SKILL.md +167 -0
- package/skills/fds-build/catalogue.md +181 -0
- package/skills/fds-build/evals/evals.json +519 -0
- package/skills/fds-build/resources/components/accordion.md +128 -0
- package/skills/fds-build/resources/components/alert.md +54 -0
- package/skills/fds-build/resources/components/animation.md +18 -0
- package/skills/fds-build/resources/components/avatar.md +10 -0
- package/skills/fds-build/resources/components/badge.md +18 -0
- package/skills/fds-build/resources/components/box-container.md +61 -0
- package/skills/fds-build/resources/components/breadcrumb.md +60 -0
- package/skills/fds-build/resources/components/button.md +74 -0
- package/skills/fds-build/resources/components/calendar.md +113 -0
- package/skills/fds-build/resources/components/card.md +43 -0
- package/skills/fds-build/resources/components/checkbox.md +53 -0
- package/skills/fds-build/resources/components/countdown-timer.md +20 -0
- package/skills/fds-build/resources/components/data-table.md +142 -0
- package/skills/fds-build/resources/components/date-navigator.md +25 -0
- package/skills/fds-build/resources/components/divider.md +23 -0
- package/skills/fds-build/resources/components/drawer.md +78 -0
- package/skills/fds-build/resources/components/error-display.md +137 -0
- package/skills/fds-build/resources/components/feedback-rating.md +61 -0
- package/skills/fds-build/resources/components/file-download.md +54 -0
- package/skills/fds-build/resources/components/file-upload.md +105 -0
- package/skills/fds-build/resources/components/filter.md +228 -0
- package/skills/fds-build/resources/components/footer.md +94 -0
- package/skills/fds-build/resources/components/form-date-input.md +85 -0
- package/skills/fds-build/resources/components/form-date-range-input.md +146 -0
- package/skills/fds-build/resources/components/form-e-signature.md +55 -0
- package/skills/fds-build/resources/components/form-input.md +65 -0
- package/skills/fds-build/resources/components/form-masked-input.md +69 -0
- package/skills/fds-build/resources/components/form-multi-select.md +100 -0
- package/skills/fds-build/resources/components/form-nested-multi-select.md +159 -0
- package/skills/fds-build/resources/components/form-nested-select.md +151 -0
- package/skills/fds-build/resources/components/form-otp-verification.md +175 -0
- package/skills/fds-build/resources/components/form-phone-number-input.md +87 -0
- package/skills/fds-build/resources/components/form-predictive-text-input.md +108 -0
- package/skills/fds-build/resources/components/form-select.md +103 -0
- package/skills/fds-build/resources/components/form-slider.md +116 -0
- package/skills/fds-build/resources/components/form-textarea.md +116 -0
- package/skills/fds-build/resources/components/form-time-range-picker.md +98 -0
- package/skills/fds-build/resources/components/form-timepicker.md +77 -0
- package/skills/fds-build/resources/components/form-unit-number-input.md +138 -0
- package/skills/fds-build/resources/components/fullscreen-image-carousel.md +159 -0
- package/skills/fds-build/resources/components/histogram-slider.md +41 -0
- package/skills/fds-build/resources/components/icon.md +167 -0
- package/skills/fds-build/resources/components/image-button.md +12 -0
- package/skills/fds-build/resources/components/input.md +51 -0
- package/skills/fds-build/resources/components/language-switcher.md +15 -0
- package/skills/fds-build/resources/components/layout.md +156 -0
- package/skills/fds-build/resources/components/link-list.md +120 -0
- package/skills/fds-build/resources/components/local-nav.md +119 -0
- package/skills/fds-build/resources/components/masthead.md +14 -0
- package/skills/fds-build/resources/components/menu.md +109 -0
- package/skills/fds-build/resources/components/modal.md +150 -0
- package/skills/fds-build/resources/components/navbar.md +270 -0
- package/skills/fds-build/resources/components/notification-banner.md +106 -0
- package/skills/fds-build/resources/components/pagination.md +104 -0
- package/skills/fds-build/resources/components/pill.md +55 -0
- package/skills/fds-build/resources/components/popover.md +80 -0
- package/skills/fds-build/resources/components/progress-indicator.md +47 -0
- package/skills/fds-build/resources/components/radio-button.md +66 -0
- package/skills/fds-build/resources/components/schedule.md +78 -0
- package/skills/fds-build/resources/components/sidenav.md +183 -0
- package/skills/fds-build/resources/components/singpass-button.md +14 -0
- package/skills/fds-build/resources/components/smart-app-banner.md +28 -0
- package/skills/fds-build/resources/components/tab.md +107 -0
- package/skills/fds-build/resources/components/table.md +65 -0
- package/skills/fds-build/resources/components/tag.md +68 -0
- package/skills/fds-build/resources/components/text-list.md +44 -0
- package/skills/fds-build/resources/components/time-slot-bar-week.md +66 -0
- package/skills/fds-build/resources/components/time-slot-bar.md +57 -0
- package/skills/fds-build/resources/components/time-slot-week-view.md +70 -0
- package/skills/fds-build/resources/components/timeline.md +69 -0
- package/skills/fds-build/resources/components/timetable.md +123 -0
- package/skills/fds-build/resources/components/toast.md +72 -0
- package/skills/fds-build/resources/components/toggle.md +119 -0
- package/skills/fds-build/resources/components/typography.md +86 -0
- package/skills/fds-build/resources/components/uneditable-section.md +84 -0
- package/skills/fds-build/resources/foundations/layout-composition-patterns.md +53 -0
- package/skills/fds-build/resources/foundations/tokens.md +547 -0
- package/skills/fds-build/resources/patterns/states.md +300 -0
- package/skills/fds-build/resources/setup/setup.md +202 -0
- package/skills/fds-build/resources/templates/form.md +427 -0
- package/skills/fds-build/resources/templates/listing.md +435 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "fds-build",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 0,
|
|
6
|
+
"prompt": "Build a registration form page using @lifesg/react-design-system v4. The form should collect: full name, email, phone number, and date of birth. Include field validation — all fields required, email must be valid format. Show an error summary at the top if submission fails. Show a success Alert after successful submission. Include Navbar and Footer.",
|
|
7
|
+
"expected_output": "A React component using ThemeProvider from @lifesg/react-design-system/theme, Navbar, Footer, Layout.Container type=grid, Form.Input for name/email/phone, Form.DateInput for DOB, Button.Default type=submit with loading+disabled during async, Alert type=error for summary, Alert type=success after submission. errorMessage only set after validation fails. No raw <input> elements. No styled-components. No Tailwind.",
|
|
8
|
+
"files": [],
|
|
9
|
+
"assertions": [
|
|
10
|
+
{
|
|
11
|
+
"id": "uses-theme-provider",
|
|
12
|
+
"description": "Wraps app in ThemeProvider from @lifesg/react-design-system/theme",
|
|
13
|
+
"type": "contains",
|
|
14
|
+
"value": "ThemeProvider"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "uses-form-input",
|
|
18
|
+
"description": "Uses Form.Input not raw <input>",
|
|
19
|
+
"type": "contains",
|
|
20
|
+
"value": "Form.Input"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "no-raw-input",
|
|
24
|
+
"description": "Does not use raw <input> elements",
|
|
25
|
+
"type": "not_contains",
|
|
26
|
+
"value": "<input"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "uses-form-date-input",
|
|
30
|
+
"description": "Uses Form.DateInput for date of birth",
|
|
31
|
+
"type": "contains",
|
|
32
|
+
"value": "Form.DateInput"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "uses-alert-error",
|
|
36
|
+
"description": "Uses Alert type=error for validation summary",
|
|
37
|
+
"type": "contains",
|
|
38
|
+
"value": "type=\"error\""
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "uses-alert-success",
|
|
42
|
+
"description": "Uses Alert type=success after submission",
|
|
43
|
+
"type": "contains",
|
|
44
|
+
"value": "type=\"success\""
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "button-loading-disabled",
|
|
48
|
+
"description": "Submit button uses both loading and disabled during async",
|
|
49
|
+
"type": "contains",
|
|
50
|
+
"value": "loading"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "uses-navbar",
|
|
54
|
+
"description": "Page includes Navbar",
|
|
55
|
+
"type": "contains",
|
|
56
|
+
"value": "Navbar"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "uses-footer",
|
|
60
|
+
"description": "Page includes Footer",
|
|
61
|
+
"type": "contains",
|
|
62
|
+
"value": "Footer"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "uses-layout-container",
|
|
66
|
+
"description": "Uses Layout.Container for structure",
|
|
67
|
+
"type": "contains",
|
|
68
|
+
"value": "Layout.Container"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "no-styled-components",
|
|
72
|
+
"description": "Does not import styled-components in v4",
|
|
73
|
+
"type": "not_contains",
|
|
74
|
+
"value": "from \"styled-components\""
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "correct-form-import",
|
|
78
|
+
"description": "Form imported from correct subpath",
|
|
79
|
+
"type": "contains",
|
|
80
|
+
"value": "@lifesg/react-design-system/form"
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": 1,
|
|
86
|
+
"prompt": "Build a service listing page using @lifesg/react-design-system v4. Show a hero banner with a title and a search bar (location input + category dropdown + Search button). Below the hero, show a filter sidebar on the left (sort by radio buttons, category checkboxes) and a results area on the right with a result count and 3 placeholder Card components. Include Navbar and Footer.",
|
|
87
|
+
"expected_output": "A React component with Navbar, a hero section, Layout.Container, a two-column layout with Filter component on the left and results on the right. Results area shows Typography.BodyMD for count and Card components. Uses Form.Input for location, Form.Select for category, RadioButton for sort, Filter.Checkbox or Checkbox for categories. No DataTable for citizen-facing results.",
|
|
88
|
+
"files": [],
|
|
89
|
+
"assertions": [
|
|
90
|
+
{
|
|
91
|
+
"id": "uses-navbar-footer",
|
|
92
|
+
"description": "Includes Navbar and Footer",
|
|
93
|
+
"type": "contains",
|
|
94
|
+
"value": "Navbar"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "uses-filter",
|
|
98
|
+
"description": "Uses Filter component for sidebar",
|
|
99
|
+
"type": "contains",
|
|
100
|
+
"value": "Filter"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "uses-card-not-datatable",
|
|
104
|
+
"description": "Uses Card for citizen-facing results, not DataTable",
|
|
105
|
+
"type": "contains",
|
|
106
|
+
"value": "Card"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "no-datatable",
|
|
110
|
+
"description": "Does not use DataTable for citizen-facing listing",
|
|
111
|
+
"type": "not_contains",
|
|
112
|
+
"value": "DataTable"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "shows-result-count",
|
|
116
|
+
"description": "Shows a result count using Typography",
|
|
117
|
+
"type": "contains",
|
|
118
|
+
"value": "result"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "uses-form-input-search",
|
|
122
|
+
"description": "Search bar uses Form.Input",
|
|
123
|
+
"type": "contains",
|
|
124
|
+
"value": "Form.Input"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "uses-form-select",
|
|
128
|
+
"description": "Category uses Form.Select not raw <select>",
|
|
129
|
+
"type": "contains",
|
|
130
|
+
"value": "Form.Select"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "no-raw-select",
|
|
134
|
+
"description": "Does not use raw <select>",
|
|
135
|
+
"type": "not_contains",
|
|
136
|
+
"value": "<select"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "two-column-layout",
|
|
140
|
+
"description": "Uses a two-column layout structure",
|
|
141
|
+
"type": "contains",
|
|
142
|
+
"value": "Layout.Container"
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": 2,
|
|
148
|
+
"prompt": "I need a delete confirmation modal using @lifesg/react-design-system v4. A button labelled 'Delete item' opens a modal. The modal has a title 'Delete this item?', a description 'This action cannot be undone.', a Cancel button and a red Confirm button. Clicking either closes the modal.",
|
|
149
|
+
"expected_output": "Uses Modal + Modal.Box with show controlled by useState. enableOverlayClick + onOverlayClick both call the same close handler. Modal.Box has showCloseButton and onClose. Direct child of Modal is not a Fragment. Confirm button uses danger prop. Both Cancel and Confirm call setShow(false).",
|
|
150
|
+
"files": [],
|
|
151
|
+
"assertions": [
|
|
152
|
+
{
|
|
153
|
+
"id": "uses-modal",
|
|
154
|
+
"description": "Uses Modal component",
|
|
155
|
+
"type": "contains",
|
|
156
|
+
"value": "Modal"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "uses-modal-box",
|
|
160
|
+
"description": "Uses Modal.Box inside Modal",
|
|
161
|
+
"type": "contains",
|
|
162
|
+
"value": "Modal.Box"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "controlled-show",
|
|
166
|
+
"description": "show prop controlled by state",
|
|
167
|
+
"type": "contains",
|
|
168
|
+
"value": "show="
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "has-close-button",
|
|
172
|
+
"description": "Modal.Box has showCloseButton",
|
|
173
|
+
"type": "contains",
|
|
174
|
+
"value": "showCloseButton"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "uses-danger",
|
|
178
|
+
"description": "Confirm/delete button uses danger prop",
|
|
179
|
+
"type": "contains",
|
|
180
|
+
"value": "danger"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "no-fragment-child",
|
|
184
|
+
"description": "Does not use Fragment as direct child of Modal",
|
|
185
|
+
"type": "not_contains",
|
|
186
|
+
"value": "<>"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "overlay-click-handled",
|
|
190
|
+
"description": "Overlay click handled via enableOverlayClick + onOverlayClick",
|
|
191
|
+
"type": "contains",
|
|
192
|
+
"value": "onOverlayClick"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"id": 3,
|
|
198
|
+
"prompt": "Build a dashboard page using @lifesg/react-design-system v4. Show 4 KPI metric cards in a responsive grid (2 columns on tablet, 4 on desktop). Each card shows a metric label, a large number value, and a percentage change badge. Below the cards, show a full-width section heading and a placeholder for a chart. Include Navbar and Footer.",
|
|
199
|
+
"expected_output": "Uses Layout.Container type=grid with Layout.ColDiv xsCols=8 mdCols=6 lgCols=3 for the 4-column responsive grid. Metric values use Typography.HeadingXL or similar large heading. Badge component for percentage change. Colour tokens used, not hardcoded hex. No Tailwind classes.",
|
|
200
|
+
"files": [],
|
|
201
|
+
"assertions": [
|
|
202
|
+
{
|
|
203
|
+
"id": "uses-layout-grid",
|
|
204
|
+
"description": "Uses Layout.Container type=grid",
|
|
205
|
+
"type": "contains",
|
|
206
|
+
"value": "type=\"grid\""
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"id": "responsive-cols",
|
|
210
|
+
"description": "ColDiv uses xsCols for mobile stacking",
|
|
211
|
+
"type": "contains",
|
|
212
|
+
"value": "xsCols"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"id": "uses-desktop-cols",
|
|
216
|
+
"description": "ColDiv uses lgCols for 4-column desktop layout",
|
|
217
|
+
"type": "contains",
|
|
218
|
+
"value": "lgCols"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "uses-card",
|
|
222
|
+
"description": "Metric cards use Card component",
|
|
223
|
+
"type": "contains",
|
|
224
|
+
"value": "Card"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"id": "uses-badge",
|
|
228
|
+
"description": "Percentage change uses Badge component",
|
|
229
|
+
"type": "contains",
|
|
230
|
+
"value": "Badge"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"id": "uses-navbar-footer",
|
|
234
|
+
"description": "Includes Navbar and Footer",
|
|
235
|
+
"type": "contains",
|
|
236
|
+
"value": "Navbar"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "no-hardcoded-hex",
|
|
240
|
+
"description": "Does not hardcode hex colour values",
|
|
241
|
+
"type": "not_contains",
|
|
242
|
+
"value": "#"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"id": "no-tailwind",
|
|
246
|
+
"description": "Does not use Tailwind class names",
|
|
247
|
+
"type": "not_contains",
|
|
248
|
+
"value": "className=\"flex"
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"id": 4,
|
|
254
|
+
"prompt": "I need to show a 'no results found' state on my listing page using @lifesg/react-design-system. The search returned zero results. Show the appropriate empty state with a 'Clear filters' action button.",
|
|
255
|
+
"expected_output": "Uses ErrorDisplay component with type='no-item-found'. Imports from @lifesg/react-design-system/error-display. Provides actionButton prop with children='Clear filters' and onClick handler. Does not build a custom empty state with raw Typography + Button.",
|
|
256
|
+
"files": [],
|
|
257
|
+
"assertions": [
|
|
258
|
+
{
|
|
259
|
+
"id": "uses-error-display",
|
|
260
|
+
"description": "Uses ErrorDisplay component, not a custom empty state",
|
|
261
|
+
"type": "contains",
|
|
262
|
+
"value": "ErrorDisplay"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"id": "correct-type",
|
|
266
|
+
"description": "Uses type='no-item-found'",
|
|
267
|
+
"type": "contains",
|
|
268
|
+
"value": "no-item-found"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"id": "correct-import",
|
|
272
|
+
"description": "Imports from error-display subpath",
|
|
273
|
+
"type": "contains",
|
|
274
|
+
"value": "error-display"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"id": "has-action-button",
|
|
278
|
+
"description": "Includes actionButton prop for clear filters",
|
|
279
|
+
"type": "contains",
|
|
280
|
+
"value": "actionButton"
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"id": 5,
|
|
286
|
+
"prompt": "Add a loading state to my page. While data is fetching, show a full-page loading spinner centred in a card. Once loaded, show the content.",
|
|
287
|
+
"expected_output": "Uses LoadingSpinner imported from @lifesg/react-design-system/animations (not invented or from another library). Wrapped in a Card with centred layout. Conditional render based on isLoading state.",
|
|
288
|
+
"files": [],
|
|
289
|
+
"assertions": [
|
|
290
|
+
{
|
|
291
|
+
"id": "uses-loading-spinner",
|
|
292
|
+
"description": "Uses LoadingSpinner component",
|
|
293
|
+
"type": "contains",
|
|
294
|
+
"value": "LoadingSpinner"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "correct-import",
|
|
298
|
+
"description": "LoadingSpinner imported from animations subpath",
|
|
299
|
+
"type": "contains",
|
|
300
|
+
"value": "@lifesg/react-design-system/animations"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"id": "conditional-render",
|
|
304
|
+
"description": "Loading state conditionally rendered",
|
|
305
|
+
"type": "contains",
|
|
306
|
+
"value": "isLoading"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"id": "no-custom-spinner",
|
|
310
|
+
"description": "Does not invent a custom spinner with raw CSS",
|
|
311
|
+
"type": "not_contains",
|
|
312
|
+
"value": "animation: spin"
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"id": 6,
|
|
318
|
+
"prompt": "Build a registration form using @lifesg/react-design-system v3. Collect full name, email, and a date of birth. On submit, show a success Toast. Include Navbar and Footer.",
|
|
319
|
+
"expected_output": "Uses StyleSheetManager + ThemeProvider with a theme object (e.g. LifeSGTheme) from @lifesg/react-design-system/theme. Uses styled-components for custom layout. Form.Input for name/email. Form.DateInput for DOB. Toast component for success feedback. No raw <input> elements. No plain CSS variables — tokens are interpolation functions inside styled templates.",
|
|
320
|
+
"files": [],
|
|
321
|
+
"assertions": [
|
|
322
|
+
{
|
|
323
|
+
"id": "uses-stylesheet-manager",
|
|
324
|
+
"description": "Wraps app in StyleSheetManager (v3 requirement)",
|
|
325
|
+
"type": "contains",
|
|
326
|
+
"value": "StyleSheetManager"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"id": "theme-object-not-string",
|
|
330
|
+
"description": "ThemeProvider receives a theme object, not a string",
|
|
331
|
+
"type": "contains",
|
|
332
|
+
"value": "LifeSGTheme"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"id": "uses-form-input",
|
|
336
|
+
"description": "Uses Form.Input for text fields",
|
|
337
|
+
"type": "contains",
|
|
338
|
+
"value": "Form.Input"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"id": "uses-form-date-input",
|
|
342
|
+
"description": "Uses Form.DateInput for date of birth",
|
|
343
|
+
"type": "contains",
|
|
344
|
+
"value": "Form.DateInput"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"id": "uses-toast",
|
|
348
|
+
"description": "Uses Toast for success feedback",
|
|
349
|
+
"type": "contains",
|
|
350
|
+
"value": "Toast"
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"id": "no-css-variable-strings",
|
|
354
|
+
"description": "Does not use plain CSS variable strings (v4 pattern) in styled-components",
|
|
355
|
+
"type": "not_contains",
|
|
356
|
+
"value": "var(--fds-"
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"id": "uses-subpath-imports",
|
|
360
|
+
"description": "Imports use subpath pattern",
|
|
361
|
+
"type": "contains",
|
|
362
|
+
"value": "@lifesg/react-design-system/form"
|
|
363
|
+
}
|
|
364
|
+
]
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"id": 7,
|
|
368
|
+
"prompt": "Build a Form.Select dropdown using @lifesg/react-design-system v4. The options are country objects: { value: 'sg', label: 'Singapore' }, { value: 'my', label: 'Malaysia' }. When the user selects an option, the closed selector should show the country name (e.g. 'Singapore'), not the code (e.g. 'sg').",
|
|
369
|
+
"expected_output": "Form.Select with options array, valueExtractor returning opt.value, listExtractor returning opt.label, and displayValueExtractor returning opt.label. Without displayValueExtractor the selector shows the raw value ('sg') instead of the label. All three extractors must be present.",
|
|
370
|
+
"files": [],
|
|
371
|
+
"assertions": [
|
|
372
|
+
{
|
|
373
|
+
"id": "uses-form-select",
|
|
374
|
+
"description": "Uses Form.Select component",
|
|
375
|
+
"type": "contains",
|
|
376
|
+
"value": "Form.Select"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"id": "has-value-extractor",
|
|
380
|
+
"description": "Provides valueExtractor",
|
|
381
|
+
"type": "contains",
|
|
382
|
+
"value": "valueExtractor"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"id": "has-list-extractor",
|
|
386
|
+
"description": "Provides listExtractor",
|
|
387
|
+
"type": "contains",
|
|
388
|
+
"value": "listExtractor"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"id": "has-display-value-extractor",
|
|
392
|
+
"description": "Provides displayValueExtractor to show label not raw value in closed selector",
|
|
393
|
+
"type": "contains",
|
|
394
|
+
"value": "displayValueExtractor"
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"id": "extractor-returns-label",
|
|
398
|
+
"description": "displayValueExtractor returns the label field, not value",
|
|
399
|
+
"type": "contains",
|
|
400
|
+
"value": "opt.label"
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"id": 8,
|
|
406
|
+
"prompt": "I need a read-only summary table showing 3 rows of data: Name, Status, and Date. No sorting or row selection needed. Use @lifesg/react-design-system.",
|
|
407
|
+
"expected_output": "Uses Table.Container wrapping Table with Table.Head, Table.Body, Table.Row, Table.HeaderCell and Table.Cell sub-components. Imports from @lifesg/react-design-system/table. Does not use DataTable (overkill for read-only data). Does not use raw <table> HTML.",
|
|
408
|
+
"files": [],
|
|
409
|
+
"assertions": [
|
|
410
|
+
{
|
|
411
|
+
"id": "uses-table-container",
|
|
412
|
+
"description": "Wraps table in Table.Container for scroll behaviour",
|
|
413
|
+
"type": "contains",
|
|
414
|
+
"value": "Table.Container"
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"id": "uses-table-head",
|
|
418
|
+
"description": "Uses Table.Head for header row",
|
|
419
|
+
"type": "contains",
|
|
420
|
+
"value": "Table.Head"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"id": "uses-table-cell",
|
|
424
|
+
"description": "Uses Table.Cell for data cells",
|
|
425
|
+
"type": "contains",
|
|
426
|
+
"value": "Table.Cell"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"id": "correct-import",
|
|
430
|
+
"description": "Imports Table from the table subpath",
|
|
431
|
+
"type": "contains",
|
|
432
|
+
"value": "@lifesg/react-design-system/table"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"id": "no-datatable",
|
|
436
|
+
"description": "Does not use DataTable for a simple read-only table",
|
|
437
|
+
"type": "not_contains",
|
|
438
|
+
"value": "DataTable"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"id": "no-raw-table",
|
|
442
|
+
"description": "Does not use raw <table> HTML elements",
|
|
443
|
+
"type": "not_contains",
|
|
444
|
+
"value": "<table"
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"id": 9,
|
|
450
|
+
"prompt": "Add a back arrow icon and a settings gear icon to a page header using @lifesg/react-design-system. The back arrow should be on the left, the gear on the right.",
|
|
451
|
+
"expected_output": "Imports icons from @lifesg/react-icons subpaths: ArrowLeftIcon from @lifesg/react-icons/arrow-left and GearIcon from @lifesg/react-icons/gear. Does not import from @lifesg/react-design-system directly for icons. Does not use MUI icons when react-icons has a suitable match. Icon component names end in 'Icon'.",
|
|
452
|
+
"files": [],
|
|
453
|
+
"assertions": [
|
|
454
|
+
{
|
|
455
|
+
"id": "imports-from-react-icons",
|
|
456
|
+
"description": "Imports from @lifesg/react-icons, not the main DS package",
|
|
457
|
+
"type": "contains",
|
|
458
|
+
"value": "@lifesg/react-icons"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
"id": "uses-arrow-icon",
|
|
462
|
+
"description": "Uses a named arrow icon (not a custom SVG)",
|
|
463
|
+
"type": "contains",
|
|
464
|
+
"value": "ArrowLeft"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"id": "uses-gear-icon",
|
|
468
|
+
"description": "Uses GearIcon for settings",
|
|
469
|
+
"type": "contains",
|
|
470
|
+
"value": "Gear"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"id": "icon-names-end-in-icon",
|
|
474
|
+
"description": "Icon component names end in Icon suffix",
|
|
475
|
+
"type": "contains",
|
|
476
|
+
"value": "Icon"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"id": "no-custom-svg",
|
|
480
|
+
"description": "Does not inline a custom <svg> instead of using the package",
|
|
481
|
+
"type": "not_contains",
|
|
482
|
+
"value": "<svg"
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
"id": 10,
|
|
488
|
+
"prompt": "I have a form with a Yes/No question 'Do you agree to the terms?' and a filter panel with a 'Remember me' tick-box. Which DS components should I use for each? Use @lifesg/react-design-system v4.",
|
|
489
|
+
"expected_output": "Yes/No question in a form: use Toggle with type='yes' and type='no' (not RadioButton, not Checkbox). Filter tick-box: use Checkbox (not Toggle). Toggle is for card-style form selections; Checkbox is for compact boolean filter/table use.",
|
|
490
|
+
"files": [],
|
|
491
|
+
"assertions": [
|
|
492
|
+
{
|
|
493
|
+
"id": "uses-toggle-for-yes-no",
|
|
494
|
+
"description": "Uses Toggle for the yes/no form question",
|
|
495
|
+
"type": "contains",
|
|
496
|
+
"value": "Toggle"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"id": "toggle-type-yes",
|
|
500
|
+
"description": "Toggle uses type='yes' and type='no'",
|
|
501
|
+
"type": "contains",
|
|
502
|
+
"value": "type=\"yes\""
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"id": "uses-checkbox-for-filter",
|
|
506
|
+
"description": "Uses Checkbox for the compact filter tick-box",
|
|
507
|
+
"type": "contains",
|
|
508
|
+
"value": "Checkbox"
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
"id": "no-radio-for-yes-no",
|
|
512
|
+
"description": "Does not use RadioButton for the binary yes/no question",
|
|
513
|
+
"type": "not_contains",
|
|
514
|
+
"value": "RadioButton"
|
|
515
|
+
}
|
|
516
|
+
]
|
|
517
|
+
}
|
|
518
|
+
]
|
|
519
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Accordion
|
|
2
|
+
|
|
3
|
+
Import: `import { Accordion } from "@lifesg/react-design-system/accordion"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use to reduce visual complexity when users don't need all content at once — FAQs, supplementary details, or grouped settings. Do not use when all content needs to be visible at the same time.
|
|
8
|
+
|
|
9
|
+
## Composition
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Accordion ← container, manages expand/collapse state
|
|
13
|
+
├── Accordion.Item ← expandable panel
|
|
14
|
+
├── Accordion.Item
|
|
15
|
+
└── Accordion.Item
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
All children must be `Accordion.Item`. Do not place other elements directly inside `Accordion`.
|
|
19
|
+
|
|
20
|
+
## Variants
|
|
21
|
+
|
|
22
|
+
| `type` on `Accordion.Item` | Usage |
|
|
23
|
+
| -------------------------- | ------------------------------------ |
|
|
24
|
+
| `"default"` | Standard title size — default |
|
|
25
|
+
| `"small"` | Compact title size for dense layouts |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Accordion } from "@lifesg/react-design-system/accordion";
|
|
31
|
+
|
|
32
|
+
// Multiple items — all collapsed by default
|
|
33
|
+
const FaqSection = () => (
|
|
34
|
+
<Accordion
|
|
35
|
+
title="Frequently asked questions"
|
|
36
|
+
headingLevel={2}
|
|
37
|
+
initialDisplay="collapse-all"
|
|
38
|
+
enableExpandAll
|
|
39
|
+
>
|
|
40
|
+
<Accordion.Item title="What is this service?">
|
|
41
|
+
<p>This service lets you manage your account settings.</p>
|
|
42
|
+
</Accordion.Item>
|
|
43
|
+
<Accordion.Item title="How do I reset my password?">
|
|
44
|
+
<p>Click "Forgot password" on the login page.</p>
|
|
45
|
+
</Accordion.Item>
|
|
46
|
+
{/* JSX in title must be wrapped in <span> — title renders inside <button> */}
|
|
47
|
+
<Accordion.Item
|
|
48
|
+
title={
|
|
49
|
+
<span>
|
|
50
|
+
Can I use <strong>bold text</strong> in a title?
|
|
51
|
+
</span>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
<p>Yes — wrap JSX title content in a span.</p>
|
|
55
|
+
</Accordion.Item>
|
|
56
|
+
</Accordion>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Controlled item — force one item open regardless of Expand All state
|
|
60
|
+
const ControlledExample = () => (
|
|
61
|
+
<Accordion initialDisplay="collapse-all">
|
|
62
|
+
<Accordion.Item title="Always expanded" expanded={true}>
|
|
63
|
+
<p>This item ignores the parent initialDisplay.</p>
|
|
64
|
+
</Accordion.Item>
|
|
65
|
+
<Accordion.Item title="Follows parent state">
|
|
66
|
+
<p>This item collapses with the rest.</p>
|
|
67
|
+
</Accordion.Item>
|
|
68
|
+
</Accordion>
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Props — `AccordionProps`
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Default | Description |
|
|
75
|
+
| ------------------------ | -------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `children` \* | `JSX.Element \| JSX.Element[]` | — | — |
|
|
77
|
+
| `title` | `string \| JSX.Element` | — | Section heading rendered above the item list. Visually hidden on mobile unless `showTitleInMobile` is true. |
|
|
78
|
+
| `enableExpandAll` | `boolean` | true | Renders the "Show all" / "Hide all" toggle button. |
|
|
79
|
+
| `initialDisplay` | `"collapse-all" \| "expand-all"` | "expand-all" | Controls whether all items start expanded or collapsed. |
|
|
80
|
+
| `showTitleInMobile` | `boolean` | false | When `true`, the `title` remains visible on mobile viewports. |
|
|
81
|
+
| `id` | `string` | — | — |
|
|
82
|
+
| `className` | `string` | — | — |
|
|
83
|
+
| `headingLevel` | `number` | 2 | The ARIA heading level for the accordion and item titles. Specify a custom value to reflect the component's hierarchy within a page. When a title is provided, the title level = n and the item title level = n + 1. Otherwise, the item title level = n. |
|
|
84
|
+
| `onExpandCollapseChange` | `((expanded: boolean) => void)` | — | Called after the expand/collapse state changes. |
|
|
85
|
+
|
|
86
|
+
## Props — `AccordionItemProps`
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Default | Description |
|
|
89
|
+
| ---------------- | ------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
90
|
+
| `title` \* | `string \| JSX.Element` | — | — |
|
|
91
|
+
| `children` \* | `JSX.Element \| JSX.Element[]` | — | — |
|
|
92
|
+
| `expanded` | `boolean` | — | Controls the expanded state of this item. Omit (or pass `undefined`) to let the parent "Show all" / "Hide all" button manage the state. Pass an explicit boolean to take individual control of this item. |
|
|
93
|
+
| `id` | `string` | — | — |
|
|
94
|
+
| `className` | `string` | — | — |
|
|
95
|
+
| `type` | `AccordionItemType` | — | Visual size variant for the accordion item. |
|
|
96
|
+
| `collapsible` | `boolean` | true | When `false`, the item header is non-interactive and the content panel is always visible. |
|
|
97
|
+
| `onExpandChange` | `((expanded: boolean) => void)` | — | Called when the user toggles this item or the parent "Show all" / "Hide all" button fires. When provided, the item enters **controlled mode** — the caller must update the `expanded` prop in response. Without this prop, the item manages its own state internally (uncontrolled mode). |
|
|
98
|
+
|
|
99
|
+
## Props — `AccordionItemApi`
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default | Description |
|
|
102
|
+
| --------------- | --------------- | ------- | ----------- |
|
|
103
|
+
| `expand` \* | `() => void` | — | — |
|
|
104
|
+
| `collapse` \* | `() => void` | — | — |
|
|
105
|
+
| `isExpanded` \* | `() => boolean` | — | — |
|
|
106
|
+
|
|
107
|
+
## Props — `AccordionItemHandle`
|
|
108
|
+
|
|
109
|
+
| Prop | Type | Default | Description |
|
|
110
|
+
| --------------- | --------------- | ------- | ----------- |
|
|
111
|
+
| `expand` \* | `() => void` | — | — |
|
|
112
|
+
| `collapse` \* | `() => void` | — | — |
|
|
113
|
+
| `isExpanded` \* | `() => boolean` | — | — |
|
|
114
|
+
|
|
115
|
+
## Rules
|
|
116
|
+
|
|
117
|
+
- Always set `headingLevel` when `title` is provided — ensures correct heading hierarchy.
|
|
118
|
+
- Use `Accordion.Item` for all panels — do not place content directly in `Accordion`.
|
|
119
|
+
- Wrap custom JSX titles in `<span>` — item title renders inside a `<button>` and block elements are invalid inside it.
|
|
120
|
+
- Individual `expanded` on `Accordion.Item` overrides Expand All / Collapse All for that item.
|
|
121
|
+
- Use `initialDisplay="collapse-all"` when content is supplementary and users are unlikely to need it immediately.
|
|
122
|
+
|
|
123
|
+
## Anti-patterns
|
|
124
|
+
|
|
125
|
+
- Placing content directly inside `Accordion` without `Accordion.Item` wrappers.
|
|
126
|
+
- Using Accordion when all content should be visible at once — use a plain layout instead.
|
|
127
|
+
- Using block elements (e.g. `<div>`) directly as `title` in `Accordion.Item` — wrap in `<span>`.
|
|
128
|
+
- Skipping `headingLevel` when `title` is set — this breaks heading hierarchy for screen readers.
|