Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/checkbox-switch-single-label-node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@toptal/picasso-form-label': minor
'@toptal/picasso-checkbox': minor
'@toptal/picasso-switch': minor
---

### Checkbox, Switch

- expose a single label-associated node when a `label` is provided, so `getByLabelText`, `getByText` and `getByRole` match once. The base-ui control renders an accessible `role` element plus a hidden native `<input>`; both used to be label-associated (the `role` element via `aria-labelledby`, the input via the wrapping `<label>`), which made those queries throw "Found multiple elements" in consumer tests. The control now names itself via `aria-labelledby`. Note the DOM contract change: a labeled control's root element changes from `<label>` to `<div>`, and the derived `"<id>-label"` node id is gone β€” update selectors/styles that relied on the `label` tag or that id. Label clicks are still forwarded to preserve click-to-toggle and focus, and interactive content inside the label (links, buttons) no longer toggles the control

### FormControlLabel

- add an optional `labelId` prop. When provided, the wrapper renders as a non-`<label>` element (so a control's hidden native input is not a second label-associated node) and forwards label-text clicks to the control (skipping interactive descendants). The default `<label>` path is unchanged, so `Radio` and other consumers are unaffected
7 changes: 7 additions & 0 deletions .changeset/container-spacing-classes-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@toptal/picasso-container': patch
---

### Container

- resolve spacing classes through `@toptal/picasso-utils` β€” the resolver and class table moved there unchanged; no consumer-visible change
27 changes: 27 additions & 0 deletions .changeset/decommission-mui-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@toptal/picasso-provider': major
'@toptal/picasso': major
'@toptal/picasso-utils': patch
'@toptal/picasso-collapse': patch
'@toptal/picasso-fade': patch
'@toptal/picasso-slide': patch
---

Decommission the MUI v4 runtime and remove the `@material-ui/core` peer dependency.

`@toptal/picasso-provider` no longer ships an MUI theme runtime, and `@toptal/picasso` no longer requires `@material-ui/core` as a peer dependency. Both packages now allow React 19 (the `react`/`react-dom` peer range widens to `>=16.12.0`).

Breaking changes (consumer action required):

- a `PicassoProvider` and its `.override()`, `.extendTheme()`, and `.disableResponsiveStyle()` methods are removed. There is no runtime MUI theme β€” override component styling with Tailwind `className`/`data-*` variants instead of `PicassoProvider.override()`.
- the `theme`, `injectFirst`, and `disableClassNamePrefix` props are removed from `<Picasso>` and `<PicassoLight>` (they only configured the MUI theme and JSS style injection, both gone). For the one field with a runtime effect β€” page content width β€” set it on `<Page>` via a CSS-variable class (e.g. `<Page className="[--content-width:80em]">`) rather than the `theme` prop.
- `getServersideStylesheets` is removed. Tailwind extracts CSS at build time, so JSS server-side stylesheet collection is obsolete β€” drop it from SSR setup.
- a `@material-ui/core` is no longer a peer dependency of `@toptal/picasso`, and `@material-ui/core` / `@material-ui/utils` are no longer dependencies of `@toptal/picasso-provider`.
- the `reset` prop is removed from `<Picasso>` / `<PicassoLight>`, and the provider no longer injects a runtime reset `<style>` (`CssBaseline` is removed). The global reset now ships as CSS β€” add `@import '@toptal/picasso-tailwind/base';` to the Tailwind entry CSS, after the theme import. Omitting the import replaces `reset={false}`. The reset lives in `@layer base`, so Tailwind utilities and unlayered app CSS win over it by cascade-layer rules.
- jest snapshots rendered through `TestingPicasso` no longer contain a reset `<style>` element (expect a one-time snapshot regeneration).
- `makeResponsiveSpacingProps` is removed (org-wide audit: zero external usage; the sole in-repo consumer was Dropdown `offset`). Per-breakpoint responsive spacing now compiles to static Tailwind classes shipped in build-time CSS, so it is correct on SSR first paint. The removed implementation was client-only: server-rendered HTML carried the generated class names and CSS variables but no bridging `@media` rules until hydration β€” a silent first-paint layout shift for any SSR consumer of responsive spacing objects. With it gone, the "Tailwind extracts CSS at build time" rationale above genuinely covers every stylesheet.
- the `preventPageWidthChangeOnScrollbar` prop, the `usePreventPageWidthChangeOnScrollbar` hook (zero external consumers), and the runtime `<style>` they controlled are removed β€” **the provider no longer injects any runtime CSS**. The page-width-jump fix (`html` hides horizontal overflow, `body` spans `100vw`, from 768px up) now ships in `@toptal/picasso-tailwind/base`. Behavior deltas: under `responsive={false}` the fix previously started at 1024px, now always 768px; `<PicassoLight>` consumers importing the base entry gain the fix (PicassoLight never rendered it); `Page.TopBar` applies `md:w-screen` unconditionally to stay coordinated (override via its `className` if needed). Opt-out remedies: apps that need horizontal page scroll (e.g. signal's wide tables) re-disable it with unlayered app CSS β€” `html { overflow-x: visible }` + `body { width: 100% }` β€” which wins over `@layer base` by cascade rules, no `!important` needed (existing `!important` overrides like top-assessment's keep working and can drop the `!important`); browser extensions that inject Picasso into third-party pages (sourcing-extension) must not import the base entry into host-page context at all; test wrappers simply delete the prop. Storybook-like environments that render `<Page>` inside a story frame (a transformed ancestor re-contains the fixed TopBar while `md:w-screen`/`100vw` still sizes against the real viewport) additionally need the CSS opt-out plus `header.mui-fixed { width: 100% }` β€” see Picasso's own `.storybook/styles.css` for the reference override.

`@toptal/picasso-utils` also drops a vestigial `notistack@1.0.10` peer dependency (unused in its source) β€” it was the last transitive pull of `@material-ui/core` v4 in the tree. `@toptal/picasso-collapse`, `@toptal/picasso-fade`, and `@toptal/picasso-slide` now declare `@types/react-transition-group` directly (previously resolved transitively through `@material-ui`).

A `@toptal/picasso-codemod` transform to strip the removed `<Picasso>` props will ship alongside the consumer migration (tracked in PF-1995).
11 changes: 11 additions & 0 deletions .changeset/dropdown-offset-static.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@toptal/picasso-dropdown': patch
---

### Dropdown

- `offset` now resolves through the shared static spacing-class table from `@toptal/picasso-utils` (the same table Container's spacing props use): spacing tokens and deprecated size strings become static Tailwind margin classes; deprecated raw numbers stay inline rem styles. Computed margins are unchanged
- fix the inverted responsive-offset cascade: per-breakpoint offset objects emitted their media queries largest-first, so on wide viewports the smallest specified breakpoint won. The bug shipped with the feature; an org-wide audit found zero users of the responsive object form. With Tailwind's mobile-first variants, `offset={{ top: { sm: SPACING_2, lg: SPACING_8 } }}` now correctly applies the `lg` value on `lg`+ screens
- responsive offsets are now correct on SSR first paint β€” the classes live in the build-time Tailwind CSS, where the previous runtime `<style>` bridge was client-only (reachable in SSR HTML with `keepMounted` + `disablePortal`)
- token/string offsets moved from inline styles to classes, so consumer CSS targeting the popper can now override the offset margins (inline styles previously always won)
- responsive offsets no longer react to `disableMobileBreakpoints()` β€” they use the fixed Tailwind screens, matching Container's responsive spacing props
14 changes: 14 additions & 0 deletions .changeset/page-responsive-root-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@toptal/picasso-provider': minor
'@toptal/picasso-page': minor
---

### PicassoProvider

- expose the `responsive` flag through `RootContext` / `useAppConfig()` (new optional `responsive` prop on `PicassoGlobalStylesProvider`)

### Page

- read the non-responsive min-width from `useAppConfig()` instead of `PicassoProvider.theme.layout`, and apply it as a static Tailwind class (`min-w-[768px]`) instead of an inline style
- merge the consumer `className` after the `--content-width*` CSS variable defaults, so overrides like `<Page className='[--content-width:80em]'>` win
- in `Page.Content` and `Page.Footer` read their max-width from the `--content-width` / `--content-width-wide` variables (with the previous values as fallbacks) instead of hardcoded values
7 changes: 7 additions & 0 deletions .changeset/page-topbar-wscreen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@toptal/picasso-page': major
---

### PageTopBar

- the `Page.TopBar` header always applies `md:w-screen`, keeping the top bar coordinated with the page-width-jump fix that now ships unconditionally in `@toptal/picasso-tailwind/base`. It was previously conditional on the provider's `preventPageWidthChangeOnScrollbar` prop, which is removed. Consumers that opted out of the fix and need the old top-bar width back override via `className` (e.g. `md:w-full`) β€” consumer `className` is last in the merge, so it wins
8 changes: 8 additions & 0 deletions .changeset/picasso-tailwind-base-entry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@toptal/picasso-tailwind': minor
---

### Base layer

- add an opt-in `@toptal/picasso-tailwind/base` CSS entry that ships the Picasso global reset inside `@layer base`. Import it in the Tailwind entry CSS after the theme import (`@import '@toptal/picasso-tailwind/base';`). It replaces the runtime reset previously injected by `@toptal/picasso-provider`; omit the import to opt out (the former `<Picasso reset={false}>`). Because the reset is cascade-layered, all Tailwind utilities and any unlayered app CSS win over it.
- the base entry also ships the page-width-jump fix formerly injected at runtime by the provider (`html { width: 100%; overflow-x: hidden }` + `body { width: 100vw }` from 768px up): page width no longer changes when the vertical scrollbar appears or disappears. Opt out with unlayered app CSS β€” `html { overflow-x: visible }` + `body { width: 100% }` β€” which wins over `@layer base` by cascade rules.
7 changes: 7 additions & 0 deletions .changeset/utils-spacing-classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@toptal/picasso-utils': minor
---

### Utils

- add the spacing-class resolver β€” `getSpacingClasses`, `getSpacingStyles`, `getMappedClass`, `getResponsiveClasses` over a static, mobile-first Tailwind class table β€” moved unchanged from `@toptal/picasso-container` internals so Container spacing props and Dropdown `offset` share one implementation
21 changes: 3 additions & 18 deletions .storybook/components/CodeExample/CodeExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import React, {
} from 'react'
import debounce from 'debounce'
import styled from 'styled-components'
import { makeStyles, Theme } from '@material-ui/core/styles'
import SourceRender, { RenderResult } from 'react-source-render'
import copy from 'copy-to-clipboard'

Expand All @@ -21,16 +20,12 @@ import { Code16, Link16 } from '@toptal/picasso-icons'

import Editor from '../Editor'
import purifyFixedPosition from '../../utils/purify-fixed-position'
import styles from './styles'
import * as classes from './styles'

const COPY_LINK_DEFAULT_TEXT = 'Link'
const COPY_LINK_COPIED_TEXT = 'Copied!'
const PRESETS = [['typescript', { allExtensions: true, isTSX: true }], 'es2015']

const useStyles = makeStyles<Theme>(styles, {
name: 'PicassoCodeExample',
})

interface Props extends BaseProps {
permanentLink: string
src: string
Expand Down Expand Up @@ -175,11 +170,7 @@ const PicassoSSR: FunctionComponent = ({ children }) => {
}

return (
<Picasso
fixViewport={false}
loadFavicon={false}
preventPageWidthChangeOnScrollbar={false}
>
<Picasso fixViewport={false} loadFavicon={false}>
<Purifier>{children}</Purifier>
</Picasso>
)
Expand Down Expand Up @@ -240,7 +231,6 @@ const CodeExample = ({
permanentLink,
src,
}: Props) => {
const classes = useStyles()
const [sourceCode, setSourceCode] = useState(
getOriginalSourceCode({
src,
Expand Down Expand Up @@ -272,12 +262,7 @@ const CodeExample = ({
*/
if (TEST_ENV === 'visual') {
const renderInTestPicasso = (element: ReactNode) => (
<Picasso
loadFonts={false}
fixViewport={false}
loadFavicon={false}
preventPageWidthChangeOnScrollbar={false}
>
<Picasso loadFonts={false} fixViewport={false} loadFavicon={false}>
<Purifier>{element}</Purifier>
</Picasso>
)
Expand Down
35 changes: 8 additions & 27 deletions .storybook/components/CodeExample/styles.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,9 @@
import { createStyles } from '@material-ui/core'
export const root = 'relative w-full'

export default () =>
createStyles({
root: {
position: 'relative',
width: '100%'
},
component: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
},
componentRenderer: {
flex: 1,
position: 'relative'
},
buttons: {
top: '-.75rem',
right: '-1rem',
position: 'absolute'
},
editor: {
width: '100%',
backgroundColor: '#141414',
padding: '1rem 0'
}
})
export const component = 'flex justify-between items-center'

export const componentRenderer = 'flex-1 relative'

export const buttons = '-top-3 -right-4 absolute'

export const editor = 'w-full bg-[#141414] py-4 px-0'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we use color token for the bg?

7 changes: 1 addition & 6 deletions .storybook/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { FunctionComponent } from 'react'
import AceEditor from 'react-ace'
import { BaseProps } from '@toptal/picasso-shared'
import { makeStyles, Theme } from '@material-ui/core/styles'
import 'brace/ext/language_tools'
import 'brace/mode/jsx'
import 'brace/mode/html'
import 'brace/theme/twilight'

import styles from './styles'
import * as classes from './styles'

interface Props extends BaseProps {
id: string
Expand All @@ -16,16 +15,12 @@ interface Props extends BaseProps {
onChange: (value: string) => void
}

const useStyles = makeStyles<Theme>(styles, { name: 'PicassoEditor' })

const Editor: FunctionComponent<Props> = ({
mode = 'jsx',
id,
value,
...rest
}) => {
const classes = useStyles()

return (
<div className={classes.root}>
<AceEditor
Expand Down
14 changes: 4 additions & 10 deletions .storybook/components/Editor/styles.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
export default {
root: {
width: '100%',

'& *': {
fontFamily:
"'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace"
}
}
}
// PicassoRootNode's subtree font default is zero-specificity (`:where(.root) *`),
// so this ordinary (0,1,0) monospace rule wins naturally β€” no `!important` needed.
export const root =
'w-full [&_*]:font-[Monaco,Menlo,Ubuntu_Mono,Consolas,source-code-pro,monospace]'
7 changes: 1 addition & 6 deletions .storybook/components/Markdown/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,17 @@ import cx from 'classnames'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { BaseProps } from '@toptal/picasso-shared'
import { makeStyles, Theme } from '@material-ui/core/styles'

import styles from './styles'
import * as classes from './styles'

interface Props extends BaseProps {
children: string
className?: string
}

const useStyles = makeStyles<Theme>(styles, { name: 'PicassoMarkdown' })

const Markdown = (props: Props): JSX.Element => {
const { children, className } = props

const classes = useStyles()

return (
<ReactMarkdown
className={cx(classes.root, className)}
Expand Down
29 changes: 2 additions & 27 deletions .storybook/components/Markdown/styles.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,2 @@
export default {
root: {
'& code': {
backgroundColor: 'rgb(236, 236, 236, 0.5)',
borderRadius: '0.4em',
padding: '0.3em 0.7em',
fontWeight: 400,
fontSize: '0.8em',
fontFamily:
"'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace"
},
'& p:first-child': {
marginTop: 0
},
'& p:last-child': {
marginBottom: 0
},
'& pre': {
backgroundColor: 'rgb(236, 236, 236, 0.5)',

'& code': {
padding: '0em',
backgroundColor: 'initial'
}
}
}
}
export const root =
'[&_code]:bg-[rgb(236,236,236,0.5)] [&_code]:rounded-[0.4em] [&_code]:px-[0.7em] [&_code]:py-[0.3em] [&_code]:font-normal [&_code]:text-[0.8em] [&_code]:font-[Monaco,Menlo,Ubuntu_Mono,Consolas,source-code-pro,monospace] [&_p:first-child]:mt-0 [&_p:last-child]:mb-0 [&_pre]:bg-[rgb(236,236,236,0.5)] [&_pre_code]:p-0 [&_pre_code]:bg-[initial]'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we use color tokens if possible?

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { FunctionComponent } from 'react'
import { makeStyles, Theme } from '@material-ui/core/styles'
import { BaseProps } from '@toptal/picasso-shared'

import { PropTypeDocumentation } from '~/.storybook/utils/documentation-generator'
import cx from 'classnames'

import styles from './styles'
import { classes } from './styles'

interface Props extends BaseProps {
enums?: string[]
Expand All @@ -14,13 +13,9 @@ interface Props extends BaseProps {

const trim = (value: string) => String(value).replace(/\'|\"/gi, '')

const useStyles = makeStyles<Theme>(styles, { name: 'PicassoEnumsList' })

const EnumsList: FunctionComponent<Props> = props => {
const { enums, type } = props

const classes = useStyles()

let enumList = enums

if (!Array.isArray(enums)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import React, { FunctionComponent } from 'react'

import { makeStyles, Theme } from '@material-ui/core/styles'

import { Tooltip, Table } from '@toptal/picasso'
import { BaseProps } from '@toptal/picasso-shared'

import Markdown from '~/.storybook/components/Markdown'
import { PropTypeDocumentation } from '~/.storybook/utils/documentation-generator'
import styles from './styles'
import { classes } from './styles'

interface Props extends BaseProps {
className?: string
type: string | PropTypeDocumentation
}

const useStyles = makeStyles<Theme>(styles, {
name: 'PicassoPropTypeTableCell'
})

const PropTypeTableCell: FunctionComponent<Props> = props => {
const classes = useStyles()

const { type, className } = props

if (typeof type === 'string') {
Expand Down
Loading
Loading