Преглед изворни кода

Update Storybook controls for shared component stories and add Figma links for preview (#28634)

* Rename ui to shared

* Delete unused Switch component

* Update stories with Figma links

* Update Tag story naming for clarity

* Update Toggle button story naming for clarity

* Move shared components to the shared folder

* Remove file as part of TS migration

* Migrate jsx to tsx

* Remove file as part of TS migration

* Migrate jsx to tsx

* Include necessary controls only

* Auto SF

GitOrigin-RevId: d2458eeffa7a6b67ce522c3ccb6b4f71e5e76d62
Rebeka Dekany пре 10 месеци
родитељ
комит
cc49eeacbd
31 измењених фајлова са 583 додато и 291 уклоњено
  1. 0 27
      services/web/frontend/js/shared/components/switch.tsx
  2. 0 26
      services/web/frontend/stories/input-switch.stories.tsx
  3. 0 0
      services/web/frontend/stories/shared/badge.stories.tsx
  4. 9 4
      services/web/frontend/stories/shared/button.stories.tsx
  5. 6 8
      services/web/frontend/stories/shared/dropdown-menu.stories.tsx
  6. 1 1
      services/web/frontend/stories/shared/feedback-badge.stories.tsx
  7. 13 15
      services/web/frontend/stories/shared/form/form-check.stories.tsx
  8. 36 0
      services/web/frontend/stories/shared/form/form-input.stories.tsx
  9. 10 20
      services/web/frontend/stories/shared/form/form-radio.stories.tsx
  10. 30 0
      services/web/frontend/stories/shared/form/form-select.stories.tsx
  11. 30 0
      services/web/frontend/stories/shared/form/form-textarea.stories.tsx
  12. 4 0
      services/web/frontend/stories/shared/icon-button.stories.tsx
  13. 37 0
      services/web/frontend/stories/shared/input-switch.stories.tsx
  14. 0 0
      services/web/frontend/stories/shared/labeled-divider.stories.tsx
  15. 55 0
      services/web/frontend/stories/shared/loading-spinner.stories.tsx
  16. 0 0
      services/web/frontend/stories/shared/menu-bar.stories.tsx
  17. 6 4
      services/web/frontend/stories/shared/modal.stories.tsx
  18. 53 3
      services/web/frontend/stories/shared/notification.stories.tsx
  19. 6 2
      services/web/frontend/stories/shared/pagination.stories.jsx
  20. 13 1
      services/web/frontend/stories/shared/radio-chip.stories.tsx
  21. 0 0
      services/web/frontend/stories/shared/row.stories.tsx
  22. 10 5
      services/web/frontend/stories/shared/select.stories.tsx
  23. 4 0
      services/web/frontend/stories/shared/split-button.stories.tsx
  24. 151 0
      services/web/frontend/stories/shared/split-test-badge.stories.tsx
  25. 86 0
      services/web/frontend/stories/shared/start-free-trial-button.stories.tsx
  26. 14 4
      services/web/frontend/stories/shared/tag.stories.tsx
  27. 5 1
      services/web/frontend/stories/shared/toggle-button.stories.tsx
  28. 4 0
      services/web/frontend/stories/shared/tooltip.stories.tsx
  29. 0 0
      services/web/frontend/stories/shared/upgrade-prompt.stories.tsx
  30. 0 137
      services/web/frontend/stories/split-test-badge.stories.jsx
  31. 0 33
      services/web/frontend/stories/start-free-trial-button.stories.jsx

+ 0 - 27
services/web/frontend/js/shared/components/switch.tsx

@@ -1,27 +0,0 @@
-import classNames from 'classnames'
-
-type SwitchProps = {
-  onChange: () => void
-  checked: boolean
-  disabled?: boolean
-}
-
-function Switch({ onChange, checked, disabled = false }: SwitchProps) {
-  return (
-    // eslint-disable-next-line jsx-a11y/label-has-associated-control
-    <label className={classNames('switch-input', { disabled })}>
-      <input
-        className="invisible-input"
-        type="checkbox"
-        role="switch"
-        autoComplete="off"
-        onChange={onChange}
-        checked={checked}
-        disabled={disabled}
-      />
-      <span className="switch" />
-    </label>
-  )
-}
-
-export default Switch

+ 0 - 26
services/web/frontend/stories/input-switch.stories.tsx

@@ -1,26 +0,0 @@
-import OLFormSwitch from '@/shared/components/ol/ol-form-switch'
-import { disableControlsOf } from './utils/arg-types'
-
-export const Unchecked = () => {
-  return <OLFormSwitch onChange={() => {}} checked={false} />
-}
-
-export const UncheckedDisabled = () => {
-  return <OLFormSwitch onChange={() => {}} checked={false} disabled />
-}
-
-export const Checked = () => {
-  return <OLFormSwitch onChange={() => {}} checked />
-}
-
-export const CheckedDisabled = () => {
-  return <OLFormSwitch onChange={() => {}} checked disabled />
-}
-
-export default {
-  title: 'Shared / Components / Input Switch',
-  component: OLFormSwitch,
-  argTypes: {
-    ...disableControlsOf('inputRef'),
-  },
-}

+ 0 - 0
services/web/frontend/stories/ui/badge.stories.tsx → services/web/frontend/stories/shared/badge.stories.tsx


+ 9 - 4
services/web/frontend/stories/ui/button.stories.tsx → services/web/frontend/stories/shared/button.stories.tsx

@@ -24,7 +24,7 @@ const meta: Meta<typeof OLButton> = {
   title: 'Shared / Components / Button',
   component: OLButton,
   args: {
-    children: 'A Button',
+    children: 'Button',
     disabled: false,
     isLoading: false,
   },
@@ -47,9 +47,14 @@ const meta: Meta<typeof OLButton> = {
       ],
     },
   },
-  parameters: figmaDesignUrl(
-    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3458-22412&m=dev'
-  ),
+  parameters: {
+    controls: {
+      include: ['children', 'disabled', 'isLoading', 'size', 'variant'],
+    },
+    ...figmaDesignUrl(
+      'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3458-22412&m=dev'
+    ),
+  },
 }
 
 export default meta

+ 6 - 8
services/web/frontend/stories/ui/dropdown-menu.stories.tsx → services/web/frontend/stories/shared/dropdown-menu.stories.tsx

@@ -221,14 +221,12 @@ export const TrailingIcon = () => {
 const meta: Meta<typeof DropdownMenu> = {
   title: 'Shared / Components / DropdownMenu',
   component: DropdownMenu,
-  argTypes: {
-    disabled: {
-      control: 'boolean',
-    },
-    show: {
-      table: {
-        disable: true,
-      },
+  args: {
+    disabled: false,
+  },
+  parameters: {
+    controls: {
+      include: ['disabled'],
     },
   },
 }

+ 1 - 1
services/web/frontend/stories/feedback-badge.stories.tsx → services/web/frontend/stories/shared/feedback-badge.stories.tsx

@@ -1,4 +1,4 @@
-import { ScopeDecorator } from './decorators/scope'
+import { ScopeDecorator } from '../decorators/scope'
 import { FeedbackBadge } from '@/shared/components/feedback-badge'
 
 export const WithDefaultText = () => {

+ 13 - 15
services/web/frontend/stories/ui/form/form-check.stories.tsx → services/web/frontend/stories/shared/form/form-check.stories.tsx

@@ -1,25 +1,14 @@
 import { useRef, useLayoutEffect } from 'react'
 import { Form } from 'react-bootstrap'
 import type { Meta, StoryObj } from '@storybook/react'
+import { figmaDesignUrl } from '../../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<(typeof Form)['Check']> = {
   title: 'Shared / Components / Form',
   component: Form.Check,
-  argTypes: {
-    id: {
-      table: {
-        disable: true,
-      },
-    },
-    label: {
-      table: {
-        disable: true,
-      },
-    },
-    defaultChecked: {
-      table: {
-        disable: true,
-      },
+  parameters: {
+    controls: {
+      include: ['disabled'],
     },
   },
 }
@@ -33,6 +22,9 @@ export const Checkbox: Story = {
     label: 'Label',
     disabled: false,
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3494-247203&m=dev'
+  ),
 }
 
 export const CheckboxChecked: Story = {
@@ -42,6 +34,9 @@ export const CheckboxChecked: Story = {
     disabled: false,
     defaultChecked: true,
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3495-249055&m=dev'
+  ),
 }
 
 export const CheckboxIndeterminate = (args: Story['args']) => {
@@ -60,3 +55,6 @@ CheckboxIndeterminate.args = {
   label: 'Label',
   disabled: false,
 }
+CheckboxIndeterminate.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3495-249055&m=dev'
+)

+ 36 - 0
services/web/frontend/stories/ui/form/form-input.stories.tsx → services/web/frontend/stories/shared/form/form-input.stories.tsx

@@ -5,10 +5,16 @@ import FormText from '@/shared/components/form/form-text'
 import FormControl from '@/shared/components/form/form-control'
 import MaterialIcon from '@/shared/components/material-icon'
 import FormFeedback from '@/shared/components/form/form-feedback'
+import { figmaDesignUrl } from '../../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<React.ComponentProps<typeof FormControl>> = {
   title: 'Shared / Components / Form / Input',
   component: FormControl,
+  parameters: {
+    controls: {
+      include: ['disabled'],
+    },
+  },
 }
 export default meta
 
@@ -42,6 +48,9 @@ export const Default: Story = {
 Default.args = {
   disabled: false,
 }
+Default.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-152419&m=dev'
+)
 
 export const Info: Story = {
   render: args => {
@@ -81,7 +90,13 @@ export const Info: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Info.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-152426&m=dev'
+)
 
 export const Error: Story = {
   render: args => {
@@ -107,7 +122,13 @@ export const Error: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Error.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-166648&m=dev'
+)
 
 export const Warning: Story = {
   render: args => {
@@ -133,7 +154,13 @@ export const Warning: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Warning.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-166648&m=dev'
+)
 
 export const Success: Story = {
   render: args => {
@@ -159,7 +186,13 @@ export const Success: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Success.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-166648&m=dev'
+)
 
 export const WithIcons: Story = {
   render: args => {
@@ -252,4 +285,7 @@ export const WithIcons: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }

+ 10 - 20
services/web/frontend/stories/ui/form/form-radio.stories.tsx → services/web/frontend/stories/shared/form/form-radio.stories.tsx

@@ -1,29 +1,13 @@
 import { Form } from 'react-bootstrap'
 import type { Meta, StoryObj } from '@storybook/react'
+import { figmaDesignUrl } from '../../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<(typeof Form)['Check']> = {
   title: 'Shared / Components / Form',
   component: Form.Check,
-  argTypes: {
-    id: {
-      table: {
-        disable: true,
-      },
-    },
-    label: {
-      table: {
-        disable: true,
-      },
-    },
-    type: {
-      table: {
-        disable: true,
-      },
-    },
-    defaultChecked: {
-      table: {
-        disable: true,
-      },
+  parameters: {
+    controls: {
+      include: ['disabled'],
     },
   },
 }
@@ -38,6 +22,9 @@ export const Radio: Story = {
     label: 'Label',
     disabled: false,
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3495-259211&m=dev'
+  ),
 }
 
 export const RadioChecked: Story = {
@@ -48,4 +35,7 @@ export const RadioChecked: Story = {
     disabled: false,
     defaultChecked: true,
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3495-259218&m=dev'
+  ),
 }

+ 30 - 0
services/web/frontend/stories/ui/form/form-select.stories.tsx → services/web/frontend/stories/shared/form/form-select.stories.tsx

@@ -2,10 +2,16 @@ import { Form, FormSelectProps } from 'react-bootstrap'
 import type { Meta, StoryObj } from '@storybook/react'
 import FormGroup from '@/shared/components/form/form-group'
 import FormText from '@/shared/components/form/form-text'
+import { figmaDesignUrl } from '../../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<FormSelectProps> = {
   title: 'Shared / Components / Form / Select',
   component: Form.Select,
+  parameters: {
+    controls: {
+      include: ['disabled'],
+    },
+  },
 }
 export default meta
 
@@ -54,6 +60,9 @@ export const Default: Story = {
 Default.args = {
   disabled: false,
 }
+Default.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-199797&m=dev'
+)
 
 export const Info: Story = {
   render: args => {
@@ -94,6 +103,9 @@ export const Info: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
 
 export const Error: Story = {
@@ -135,7 +147,13 @@ export const Error: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Error.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-200123&m=dev'
+)
 
 export const Warning: Story = {
   render: args => {
@@ -176,7 +194,13 @@ export const Warning: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Warning.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-199800&m=dev'
+)
 
 export const Success: Story = {
   render: args => {
@@ -217,4 +241,10 @@ export const Success: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Success.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-199800&m=dev'
+)

+ 30 - 0
services/web/frontend/stories/ui/form/form-textarea.stories.tsx → services/web/frontend/stories/shared/form/form-textarea.stories.tsx

@@ -3,10 +3,16 @@ import type { Meta, StoryObj } from '@storybook/react'
 import FormGroup from '@/shared/components/form/form-group'
 import FormText from '@/shared/components/form/form-text'
 import FormControl from '@/shared/components/form/form-control'
+import { figmaDesignUrl } from '../../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<React.ComponentProps<typeof FormControl>> = {
   title: 'Shared / Components / Form / Textarea',
   component: FormControl,
+  parameters: {
+    controls: {
+      include: ['disabled'],
+    },
+  },
 }
 export default meta
 
@@ -50,6 +56,9 @@ export const Default: Story = {
 Default.args = {
   disabled: false,
 }
+Default.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-176397&m=dev'
+)
 
 export const Info: Story = {
   render: args => {
@@ -92,6 +101,9 @@ export const Info: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
 
 export const Error: Story = {
@@ -138,7 +150,13 @@ export const Error: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Error.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-176403&m=dev'
+)
 
 export const Warning: Story = {
   render: args => {
@@ -181,7 +199,13 @@ export const Warning: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Warning.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-176403&m=dev'
+)
 
 export const Success: Story = {
   render: args => {
@@ -224,4 +248,10 @@ export const Success: Story = {
       </>
     )
   },
+  args: {
+    disabled: false,
+  },
 }
+Success.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-176403&m=dev'
+)

+ 4 - 0
services/web/frontend/stories/ui/icon-button.stories.tsx → services/web/frontend/stories/shared/icon-button.stories.tsx

@@ -1,6 +1,7 @@
 import IconButton from '@/shared/components/button/icon-button'
 import type { Meta } from '@storybook/react'
 import { useTranslation } from 'react-i18next'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
 type Args = React.ComponentProps<typeof IconButton>
 
@@ -35,6 +36,9 @@ const meta: Meta<typeof IconButton> = {
       ],
     },
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-168934&m=dev'
+  ),
 }
 
 export default meta

+ 37 - 0
services/web/frontend/stories/shared/input-switch.stories.tsx

@@ -0,0 +1,37 @@
+import OLFormSwitch from '@/shared/components/ol/ol-form-switch'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
+
+type Args = React.ComponentProps<typeof OLFormSwitch>
+
+export const Unchecked = (args: Args) => {
+  return <OLFormSwitch onChange={() => {}} checked={false} {...args} />
+}
+
+export const UncheckedDisabled = (args: Args) => {
+  return <OLFormSwitch onChange={() => {}} checked={false} disabled {...args} />
+}
+
+export const Checked = (args: Args) => {
+  return <OLFormSwitch onChange={() => {}} checked {...args} />
+}
+
+export const CheckedDisabled = (args: Args) => {
+  return <OLFormSwitch onChange={() => {}} checked disabled {...args} />
+}
+
+export default {
+  title: 'Shared / Components / Input Switch',
+  component: OLFormSwitch,
+  args: {
+    checked: false,
+    disabled: false,
+  },
+  parameters: {
+    controls: {
+      include: ['checked', 'disabled'],
+    },
+    ...figmaDesignUrl(
+      'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-139487&m=dev'
+    ),
+  },
+}

+ 0 - 0
services/web/frontend/stories/labeled-divider.stories.tsx → services/web/frontend/stories/shared/labeled-divider.stories.tsx


+ 55 - 0
services/web/frontend/stories/shared/loading-spinner.stories.tsx

@@ -0,0 +1,55 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import LoadingSpinner, {
+  FullSizeLoadingSpinner,
+} from '@/shared/components/loading-spinner'
+
+type Story = StoryObj<typeof LoadingSpinner>
+
+export const Default: Story = {
+  args: {
+    loadingText: 'Loading content...',
+  },
+}
+
+export const WithDelay: Story = {
+  args: {
+    delay: 500,
+    loadingText: 'This will appear after a 500ms delay...',
+  },
+}
+
+export const FullSize: StoryObj<typeof FullSizeLoadingSpinner> = {
+  render: args => <FullSizeLoadingSpinner {...args} />,
+  args: {
+    loadingText: 'Loading entire section...',
+    size: 'sm',
+  },
+}
+
+const meta: Meta<typeof LoadingSpinner> = {
+  title: 'Shared / Components / Loading Spinner',
+  component: LoadingSpinner,
+  parameters: {
+    layout: 'centered',
+    controls: {
+      include: ['loadingText', 'delay', 'size'],
+    },
+  },
+  argTypes: {
+    delay: {
+      control: 'select',
+      options: [0, 500],
+    },
+    size: {
+      control: 'radio',
+      options: ['lg', 'sm'],
+    },
+  },
+  args: {
+    size: 'sm',
+    delay: 0,
+  },
+  render: args => <LoadingSpinner {...args} />,
+}
+
+export default meta

+ 0 - 0
services/web/frontend/stories/menu-bar.stories.tsx → services/web/frontend/stories/shared/menu-bar.stories.tsx


+ 6 - 4
services/web/frontend/stories/modals/modal.stories.tsx → services/web/frontend/stories/shared/modal.stories.tsx

@@ -1,5 +1,5 @@
 import type { Meta, StoryObj } from '@storybook/react'
-import { figmaDesignUrl } from './../../../.storybook/utils/figma-design-url'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 import OLModal, {
   OLModalHeader,
   OLModalBody,
@@ -81,14 +81,16 @@ export const Danger: Story = {
 const meta: Meta<typeof OLModal> = {
   title: 'Shared / Components / Modal',
   component: OLModal,
+  parameters: {
+    controls: {
+      include: ['size', 'title'],
+    },
+  },
   argTypes: {
     size: {
       control: 'radio',
       options: ['lg', 'md', 'sm'],
     },
-    title: { control: 'text' },
-    children: { control: false },
-    footer: { control: false },
   },
   args: {
     show: true,

+ 53 - 3
services/web/frontend/stories/notification.stories.tsx → services/web/frontend/stories/shared/notification.stories.tsx

@@ -1,25 +1,38 @@
 import fetchMock from 'fetch-mock'
-import Notification from '../js/shared/components/notification'
-import { postJSON } from '../js/infrastructure/fetch-json'
-import useAsync from '../js/shared/hooks/use-async'
+import Notification from '../../js/shared/components/notification'
+import { postJSON } from '../../js/infrastructure/fetch-json'
+import useAsync from '../../js/shared/hooks/use-async'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
 type Args = React.ComponentProps<typeof Notification>
 
 export const NotificationInfo = (args: Args) => {
   return <Notification {...args} isDismissible />
 }
+NotificationInfo.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3488-103958&m=dev'
+)
 
 export const NotificationSuccess = (args: Args) => {
   return <Notification {...args} isDismissible type="success" />
 }
+NotificationSuccess.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-110077&m=dev'
+)
 
 export const NotificationWarning = (args: Args) => {
   return <Notification {...args} isDismissible type="warning" />
 }
+NotificationWarning.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-112036&m=dev'
+)
 
 export const NotificationError = (args: Args) => {
   return <Notification {...args} isDismissible type="error" />
 }
+NotificationError.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-112059&m=dev'
+)
 
 export const NotificationOffer = (args: Args) => {
   return <Notification {...args} isDismissible type="offer" />
@@ -43,18 +56,30 @@ export const NotificationWithActionBelowContent = (args: Args) => {
     />
   )
 }
+NotificationWithActionBelowContent.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-118816&m=dev'
+)
 
 export const NotificationWithTitle = (args: Args) => {
   return <Notification {...args} title="Some title" />
 }
+NotificationWithTitle.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-115045&m=dev'
+)
 
 export const NotificationWithAction = (args: Args) => {
   return <Notification {...args} isDismissible={false} />
 }
+NotificationWithAction.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-117190&m=dev'
+)
 
 export const NotificationDismissible = (args: Args) => {
   return <Notification {...args} action={undefined} />
 }
+NotificationDismissible.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-116677&m=dev'
+)
 
 export const APlainNotification = (args: Args) => {
   return <Notification {...args} action={undefined} isDismissible={false} />
@@ -383,5 +408,30 @@ export default {
     ),
     action: <button className="btn btn-secondary">An action</button>,
     isDismissible: true,
+    title: undefined,
+  },
+  argTypes: {
+    content: {
+      control: 'text',
+    },
+    action: {
+      control: 'text',
+    },
+    title: {
+      control: 'text',
+    },
+  },
+  parameters: {
+    controls: {
+      include: [
+        'content',
+        'title',
+        'action',
+        'ariaLive',
+        'type',
+        'isDismissible',
+        'isActionBelowContent',
+      ],
+    },
   },
 }

+ 6 - 2
services/web/frontend/stories/pagination.stories.jsx → services/web/frontend/stories/shared/pagination.stories.jsx

@@ -1,4 +1,4 @@
-import Pagination from '../js/shared/components/pagination'
+import Pagination from '../../js/shared/components/pagination'
 
 export const Interactive = args => {
   return <Pagination {...args} />
@@ -14,6 +14,10 @@ export default {
   },
   argTypes: {
     currentPage: { control: { type: 'number', min: 1, max: 10, step: 1 } },
-    totalPages: { control: { disable: true } },
+  },
+  parameters: {
+    controls: {
+      include: ['currentPage'],
+    },
   },
 }

+ 13 - 1
services/web/frontend/stories/radio-chip.stories.tsx → services/web/frontend/stories/shared/radio-chip.stories.tsx

@@ -1,4 +1,4 @@
-import RadioChip from '../js/shared/components/radio-chip'
+import RadioChip from '../../js/shared/components/radio-chip'
 
 type Args = React.ComponentProps<typeof RadioChip>
 
@@ -19,5 +19,17 @@ export default {
   component: RadioChip,
   args: {
     label: 'Option',
+    checked: false,
+    disabled: false,
+  },
+  argTypes: {
+    label: {
+      control: 'text',
+    },
+  },
+  parameters: {
+    controls: {
+      include: ['label', 'checked', 'disabled', 'name', 'value'],
+    },
   },
 }

+ 0 - 0
services/web/frontend/stories/ui/row.stories.tsx → services/web/frontend/stories/shared/row.stories.tsx


+ 10 - 5
services/web/frontend/stories/select.stories.tsx → services/web/frontend/stories/shared/select.stories.tsx

@@ -1,4 +1,4 @@
-import { Select } from '../js/shared/components/select'
+import { Select } from '../../js/shared/components/select'
 
 const items = [1, 2, 3, 4].map(index => ({
   key: index,
@@ -12,7 +12,6 @@ export const Base = () => {
       items={items}
       itemToString={x => String(x?.value)}
       itemToKey={x => String(x.key)}
-      defaultText="Choose an item"
     />
   )
 }
@@ -24,7 +23,6 @@ export const WithSubtitles = () => {
       itemToString={x => String(x?.value)}
       itemToKey={x => String(x.key)}
       itemToSubtitle={x => x?.group ?? ''}
-      defaultText="Choose an item"
     />
   )
 }
@@ -36,7 +34,6 @@ export const WithSelectedIcon = () => {
       itemToString={x => String(x?.value)}
       itemToKey={x => String(x.key)}
       itemToSubtitle={x => x?.group ?? ''}
-      defaultText="Choose an item"
       selectedIcon
     />
   )
@@ -50,7 +47,6 @@ export const WithDisabledItem = () => {
       itemToKey={x => String(x.key)}
       itemToDisabled={x => x?.key === 1}
       itemToSubtitle={x => x?.group ?? ''}
-      defaultText="Choose an item"
     />
   )
 }
@@ -58,4 +54,13 @@ export const WithDisabledItem = () => {
 export default {
   title: 'Shared / Components / Select',
   component: Select,
+  parameters: {
+    controls: {
+      include: ['disabled', 'defaultText'],
+    },
+  },
+  args: {
+    disabled: false,
+    defaultText: 'Choose an item',
+  },
 }

+ 4 - 0
services/web/frontend/stories/ui/split-button.stories.tsx → services/web/frontend/stories/shared/split-button.stories.tsx

@@ -10,6 +10,7 @@ import {
 } from '@/shared/components/dropdown/dropdown-menu'
 import Button from '@/shared/components/button/button'
 import { ButtonGroup } from 'react-bootstrap'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
 export const Sizes = () => {
   const { t } = useTranslation()
@@ -54,6 +55,9 @@ const meta: Meta<typeof Dropdown> = {
   args: {
     align: { sm: 'start' },
   },
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-194077&m=dev'
+  ),
 }
 
 export default meta

+ 151 - 0
services/web/frontend/stories/shared/split-test-badge.stories.tsx

@@ -0,0 +1,151 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import SplitTestBadge from '../../js/shared/components/split-test-badge'
+import { SplitTestContext } from '../../js/shared/context/split-test-context'
+
+type Story = StoryObj<typeof SplitTestBadge>
+
+const splitTestContextValue = {
+  splitTestVariants: {} as Record<string, string>,
+  splitTestInfo: {} as Record<string, any>,
+}
+
+export const Alpha: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'active',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'alpha',
+        badgeInfo: {
+          url: '/alpha/participate',
+          tooltipText: 'This is an alpha feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+export const AlphaNotDisplayed: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'default',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'alpha',
+        badgeInfo: {
+          url: '/alpha/participate',
+          tooltipText: 'This is an alpha feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+export const Beta: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'active',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'beta',
+        badgeInfo: {
+          url: '/beta/participate',
+          tooltipText: 'This is a beta feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+export const BetaNotDisplayed: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'default',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'beta',
+        badgeInfo: {
+          url: '/beta/participate',
+          tooltipText: 'This is a beta feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+export const Release: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'active',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'release',
+        badgeInfo: {
+          url: '/feedback/form',
+          tooltipText: 'This is a new feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+export const ReleaseNotDisplayed: Story = {
+  render: args => {
+    splitTestContextValue.splitTestVariants = {
+      'storybook-test': 'default',
+    }
+    splitTestContextValue.splitTestInfo = {
+      'storybook-test': {
+        phase: 'release',
+        badgeInfo: {
+          url: '/feedback/form',
+          tooltipText: 'This is a new feature',
+        },
+      },
+    }
+
+    return <SplitTestBadge {...args} />
+  },
+}
+
+const meta: Meta<typeof SplitTestBadge> = {
+  title: 'Shared / Components / Split Test Badge',
+  component: SplitTestBadge,
+  parameters: {
+    controls: {
+      include: ['splitTestName', 'tooltip'],
+    },
+  },
+  argTypes: {
+    splitTestName: { control: 'text' },
+    tooltip: { control: 'text' },
+  },
+  args: {
+    splitTestName: 'storybook-test',
+    displayOnVariants: ['active'],
+  },
+  decorators: [
+    Story => (
+      <SplitTestContext.Provider value={splitTestContextValue}>
+        <Story />
+      </SplitTestContext.Provider>
+    ),
+  ],
+}
+
+export default meta

+ 86 - 0
services/web/frontend/stories/shared/start-free-trial-button.stories.tsx

@@ -0,0 +1,86 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import StartFreeTrialButton from '../../js/shared/components/start-free-trial-button'
+import type { ButtonProps } from '../../js/shared/components/types/button-props'
+
+type Args = React.ComponentProps<typeof StartFreeTrialButton> & {
+  size?: ButtonProps['size']
+}
+
+type Story = StoryObj<Args>
+
+export const Default: Story = {
+  render: args => {
+    const { size, ...startFreeTrialProps } = args
+    return (
+      <StartFreeTrialButton {...startFreeTrialProps} buttonProps={{ size }} />
+    )
+  },
+}
+
+export const CustomText: Story = {
+  render: args => {
+    const { size, ...startFreeTrialProps } = args
+    return (
+      <StartFreeTrialButton {...startFreeTrialProps} buttonProps={{ size }}>
+        Some Custom Text!
+      </StartFreeTrialButton>
+    )
+  },
+}
+
+export const ButtonStyle: Story = {
+  render: args => {
+    const { size: _size, ...startFreeTrialProps } = args
+    return (
+      <StartFreeTrialButton
+        {...startFreeTrialProps}
+        buttonProps={{
+          variant: 'secondary',
+          size: 'lg',
+        }}
+      />
+    )
+  },
+}
+
+const meta: Meta<Args> = {
+  title: 'Shared / Components / Start Free Trial Button',
+  component: StartFreeTrialButton,
+  parameters: {
+    controls: {
+      include: ['source', 'variant', 'children', 'size'],
+    },
+  },
+  argTypes: {
+    source: { control: 'text' },
+    buttonProps: { control: false },
+    children: { control: 'text' },
+    size: {
+      control: 'radio',
+      options: ['lg', 'md', 'sm'],
+    },
+    variant: {
+      control: 'radio',
+      options: [
+        'primary',
+        'secondary',
+        'ghost',
+        'danger',
+        'danger-ghost',
+        'premium',
+        'premium-secondary',
+        'link',
+      ],
+    },
+    handleClick: { control: false },
+    segmentation: { control: false },
+    extraSearchParams: { control: false },
+  },
+  args: {
+    source: 'storybook',
+    variant: 'secondary',
+    size: undefined,
+  },
+}
+
+export default meta

+ 14 - 4
services/web/frontend/stories/ui/tag.stories.tsx → services/web/frontend/stories/shared/tag.stories.tsx

@@ -1,6 +1,7 @@
 import OLTagIcon from '@/shared/components/ol/ol-tag-icon'
 import Tag from '@/shared/components/tag'
 import type { Meta, StoryObj } from '@storybook/react'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
 const meta: Meta<typeof Tag> = {
   title: 'Shared / Components / Tag',
@@ -30,19 +31,25 @@ export default meta
 
 type Story = StoryObj<typeof Tag>
 
-export const TagDefault: Story = {
+export const Default: Story = {
   render: args => {
     return <Tag {...args} />
   },
 }
+Default.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-244790&m=dev'
+)
 
-export const TagPrepend: Story = {
+export const Prepend: Story = {
   render: args => {
     return <Tag prepend={<OLTagIcon />} {...args} />
   },
 }
+Prepend.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-245386&m=dev'
+)
 
-export const TagWithCloseButton: Story = {
+export const Removable: Story = {
   render: args => {
     return (
       <Tag
@@ -55,8 +62,11 @@ export const TagWithCloseButton: Story = {
     )
   },
 }
+Removable.parameters = figmaDesignUrl(
+  'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-238534&m=dev'
+)
 
-export const TagWithContentButtonAndCloseButton: Story = {
+export const InteractiveRemovable: Story = {
   render: args => {
     return (
       <Tag

+ 5 - 1
services/web/frontend/stories/switcher.stories.tsx → services/web/frontend/stories/shared/toggle-button.stories.tsx

@@ -1,7 +1,8 @@
 import OLToggleButton from '@/shared/components/ol/ol-toggle-button'
 import OLToggleButtonGroup from '@/shared/components/ol/ol-toggle-button-group'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
-export const Base = () => {
+export const Default = () => {
   return (
     <OLToggleButtonGroup
       type="radio"
@@ -52,4 +53,7 @@ export const Disabled = () => {
 export default {
   title: 'Shared / Components / Toggle Button Group',
   component: OLToggleButtonGroup,
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-191910&m=dev'
+  ),
 }

+ 4 - 0
services/web/frontend/stories/ui/tooltip.stories.tsx → services/web/frontend/stories/shared/tooltip.stories.tsx

@@ -1,6 +1,7 @@
 import OLButton from '@/shared/components/ol/ol-button'
 import OLTooltip from '@/shared/components/ol/ol-tooltip'
 import { Meta } from '@storybook/react'
+import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
 
 export const Tooltips = () => {
   const placements = ['top', 'right', 'bottom', 'left'] as const
@@ -33,6 +34,9 @@ export const Tooltips = () => {
 const meta: Meta<typeof OLTooltip> = {
   title: 'Shared / Components / Tooltip',
   component: OLTooltip,
+  parameters: figmaDesignUrl(
+    'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3460-237285&m=dev'
+  ),
 }
 
 export default meta

+ 0 - 0
services/web/frontend/stories/upgrade-prompt.stories.tsx → services/web/frontend/stories/shared/upgrade-prompt.stories.tsx


+ 0 - 137
services/web/frontend/stories/split-test-badge.stories.jsx

@@ -1,137 +0,0 @@
-import SplitTestBadge from '../js/shared/components/split-test-badge'
-import { ScopeDecorator } from './decorators/scope'
-import { SplitTestContext } from '../js/shared/context/split-test-context'
-
-const splitTestContextValue = {
-  splitTestVariants: {
-    'storybook-test': 'active',
-  },
-  splitTestInfo: {
-    'storybook-test': {
-      phase: 'alpha',
-      badgeInfo: {
-        url: '/alpha/participate',
-        tooltipText: 'This is an alpha feature',
-      },
-    },
-  },
-}
-
-export const Alpha = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'active',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'alpha',
-      badgeInfo: {
-        url: '/alpha/participate',
-        tooltipText: 'This is an alpha feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export const AlphaNotDisplayed = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'default',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'alpha',
-      badgeInfo: {
-        url: '/alpha/participate',
-        tooltipText: 'This is an alpha feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export const Beta = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'active',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'beta',
-      badgeInfo: {
-        url: '/beta/participate',
-        tooltipText: 'This is a beta feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export const BetaNotDisplayed = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'default',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'beta',
-      badgeInfo: {
-        url: '/beta/participate',
-        tooltipText: 'This is a beta feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export const Release = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'active',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'release',
-      badgeInfo: {
-        url: '/feedback/form',
-        tooltipText: 'This is a new feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export const ReleaseNotDisplayed = args => {
-  splitTestContextValue.splitTestVariants = {
-    'storybook-test': 'default',
-  }
-  splitTestContextValue.splitTestInfo = {
-    'storybook-test': {
-      phase: 'release',
-      badgeInfo: {
-        url: '/feedback/form',
-        tooltipText: 'This is a new feature',
-      },
-    },
-  }
-
-  return <SplitTestBadge {...args} />
-}
-
-export default {
-  title: 'Shared / Components / Split Test Badge',
-  component: SplitTestBadge,
-  args: {
-    splitTestName: 'storybook-test',
-    displayOnVariants: ['active'],
-  },
-  decorators: [
-    Story => (
-      <SplitTestContext.Provider value={splitTestContextValue}>
-        <Story />
-      </SplitTestContext.Provider>
-    ),
-    ScopeDecorator,
-  ],
-}

+ 0 - 33
services/web/frontend/stories/start-free-trial-button.stories.jsx

@@ -1,33 +0,0 @@
-import StartFreeTrialButton from '../js/shared/components/start-free-trial-button'
-import { ScopeDecorator } from './decorators/scope'
-
-export const Default = args => {
-  return <StartFreeTrialButton {...args} />
-}
-
-export const CustomText = args => {
-  return (
-    <StartFreeTrialButton {...args}>Some Custom Text!</StartFreeTrialButton>
-  )
-}
-
-export const ButtonStyle = args => {
-  return (
-    <StartFreeTrialButton
-      {...args}
-      buttonProps={{
-        variant: 'danger',
-        size: 'lg',
-      }}
-    />
-  )
-}
-
-export default {
-  title: 'Shared / Components / Start Free Trial Button',
-  component: StartFreeTrialButton,
-  args: {
-    source: 'storybook',
-  },
-  decorators: [ScopeDecorator],
-}