Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/four-pandas-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

Hidden component
56 changes: 56 additions & 0 deletions docs/content/drafts/Hidden.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: Hidden
description: Use Hidden to responsively hide or show content in narrow, regular and wide viewports.
source: https://github.com/primer/react/blob/main/src/Hidden/index.tsx
status: Alpha
componentId: hidden
---

The `Hidden` component is a utility component that will help you hide or show content/components in different viewports.
**Attention:** Use Hidden only to control behaviour in a responsive manner. It does not take any `sx` props.

## Example

```jsx live
<Hidden when="narrow">
Copy link
Member

Choose a reason for hiding this comment

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

Based on the implementation below, will this be changing to on?

<Placeholder height="80px" label="This is not visible in narrow viewport" />
</Hidden>
```

## Array as `when` prop

```jsx live
<Hidden when={['narrow', 'wide']}>
<Placeholder height="80px" label="This is not visible in narrow and wide viewport" />
</Hidden>
```

## Props

<PropsTable>
<PropsTableRow
name="when"
type="string or Array"
description="Can be one or more values of 'narrow', 'wide', 'regular'"
/>
</PropsTable>

## Status

<ComponentChecklist
items={{
propsDocumented: true,
noUnnecessaryDeps: true,
adaptsToThemes: true,
adaptsToScreenSizes: true,
fullTestCoverage: false,
usedInProduction: false,
usageExamplesDocumented: true,
designReviewed: false,
a11yReviewed: false,
stableApi: false,
addressedApiFeedback: false,
hasDesignGuidelines: false,
hasFigmaComponent: false
}}
/>
2 changes: 2 additions & 0 deletions docs/src/@primer/gatsby-theme-doctocat/nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@
children:
- title: Dialog v2
url: /drafts/Dialog
- title: Hidden
url: /Hidden
- title: InlineAutocomplete
url: /drafts/InlineAutocomplete
- title: MarkdownEditor
Expand Down
54 changes: 54 additions & 0 deletions src/Hidden/Hidden.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {Meta} from '@storybook/react'
import React from 'react'
import {ThemeProvider} from '..'
import {Hidden} from './Hidden'
import BaseStyles from '../BaseStyles'
import Box from '../Box'

const meta: Meta = {
title: 'Layout components/Hidden',
component: Hidden,
decorators: [
(Story: React.ComponentType<React.PropsWithChildren<unknown>>): JSX.Element => (
<ThemeProvider>
<BaseStyles>
<Story />
</BaseStyles>
</ThemeProvider>
)
],
parameters: {
controls: {
expanded: true
}
},
argTypes: {
on: {
type: {
name: 'enum',
value: ['narrow', 'regular', 'wide']
},
defaultValue: 'regular',
control: {type: 'radio'}
}
}
}
export default meta

export const isVisibleInRegularOnly = () => (
<Box>
<Hidden on={['narrow', 'wide']}> This value is only shown in regular viewport</Hidden>
</Box>
)

export const isVisibleInNarrowOnly = () => (
<Box>
<Hidden on={['regular', 'wide']}> This value is only shown in narrow viewport</Hidden>
</Box>
)

export const isHiddenInNarrowOnly = () => (
<Box>
<Hidden on="narrow">This is hidden in narrow only</Hidden>
</Box>
)
25 changes: 25 additions & 0 deletions src/Hidden/Hidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import {useMedia} from '../hooks/useMedia'
import {viewportRanges} from '../hooks/useResponsiveValue'

type Viewports = 'narrow' | 'regular' | 'wide'

type HiddenProps = {
on: Array<Viewports> | Viewports
children: React.ReactNode
}

export const Hidden = ({on: hiddenViewports, children}: HiddenProps) => {
Copy link
Member

Choose a reason for hiding this comment

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

I loved the when convention from the docs, it was nice to read through it and think: "Okay, this is hidden when the viewport is narrow and wide". Is there a mental scheme you'd recommend with on?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kept going back and forth with when and on in my head. Sorry for the mix-up. Vini was also leaning towards when. Will change to that.
For me its just a shorter sentence to say hidden on narrow viewport vs hidden when viewport is narrow. When I think of it, I feel like its a non-native english speaker thing. 🤣

Copy link
Member

Choose a reason for hiding this comment

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

Would we need to worry about providing a default value for SSR in this exploration?

const isNarrowViewport = useMedia(viewportRanges.narrow)
const isRegularViewport = useMedia(viewportRanges.regular)
const isWideViewport = useMedia(viewportRanges.wide)
Comment on lines +13 to +15
Copy link
Member

Choose a reason for hiding this comment

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

Do you think it'd be possible to generate one media query string from the given viewports? Would be cool to do something like:

function mediaQuery(rangeOrRanges: Array<Viewports> | Viewports) {
  const ranges = Array.isArray(rangeOrRanges) ? rangeOrRanges : [rangeOrRanges]
  return ranges.map(range => viewportRanges[range]).join(', ')
}

export const Hidden = ({on: hiddenViewports, children}: HiddenProps) => {
  const matches = useMedia(mediaQuery(hiddenViewports))
  if (matches) {
    return null
  }
  return children
}

Basically any time one of the media queries from on is true, we know it should hidden, otherwise return the children.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, its a good optimisation!

let show = true
if (isNarrowViewport && (hiddenViewports === 'narrow' || hiddenViewports.indexOf('narrow') > -1)) {
show = false
} else if (isRegularViewport && (hiddenViewports === 'regular' || hiddenViewports.indexOf('regular') > -1)) {
show = false
} else if (isWideViewport && (hiddenViewports === 'wide' || hiddenViewports.indexOf('wide') > -1)) {
show = false
}
return show ? <>{children}</> : null
}
3 changes: 3 additions & 0 deletions src/Hidden/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {Hidden} from './Hidden'

export default Hidden
2 changes: 2 additions & 0 deletions src/drafts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
export * from '../Dialog/Dialog'

export * from '../Hidden' // Will be moved from drafts to main bundle after utility is proven

export {default as InlineAutocomplete} from './InlineAutocomplete'
export type {
InlineAutocompleteProps,
Expand Down