Enforce that text is wrapped in <Text>, remaining cases (#3421)
* Toggle.Button -> Toggle.ButtonWithText * Simplify Prompt.Cancel/Action * Move lines down for better diff * Remove ButtonWithText * Simplify types * Enforce Button/ButtonText nesting * Add suggested wrapper in linter error * Check <Trans> ancestry too * Also check literals * Rm ts-ignorezio/stable
parent
49266c355e
commit
46c112edfd
15
.eslintrc.js
15
.eslintrc.js
|
@ -23,17 +23,12 @@ module.exports = {
|
|||
'bsky-internal/avoid-unwrapped-text': [
|
||||
'error',
|
||||
{
|
||||
impliedTextComponents: [
|
||||
'Button', // TODO: Not always safe.
|
||||
'H1',
|
||||
'H2',
|
||||
'H3',
|
||||
'H4',
|
||||
'H5',
|
||||
'H6',
|
||||
'P',
|
||||
],
|
||||
impliedTextComponents: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P'],
|
||||
impliedTextProps: [],
|
||||
suggestedTextWrappers: {
|
||||
Button: 'ButtonText',
|
||||
'ToggleButton.Button': 'ToggleButton.ButtonText',
|
||||
},
|
||||
},
|
||||
],
|
||||
'simple-import-sort/imports': [
|
||||
|
|
|
@ -199,7 +199,7 @@ describe('avoid-unwrapped-text', () => {
|
|||
|
||||
{
|
||||
code: `
|
||||
<View prop={
|
||||
<View propText={
|
||||
<Trans><Text>foo</Text></Trans>
|
||||
}>
|
||||
<Bar />
|
||||
|
@ -281,6 +281,170 @@ function MyText({ foo }) {
|
|||
}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text>{'foo'}</Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text>{foo + 'foo'}</Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text><Trans>{'foo'}</Trans></Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo['bar'] && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{(foo === 'bar') && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{(foo !== 'bar') && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text>{\`foo\`}</Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text><Trans>{\`foo\`}</Trans></Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text>{_(msg\`foo\`)}</Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Text><Trans>{_(msg\`foo\`)}</Trans></Text>
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<Foo>
|
||||
<View prop={stuff('foo')}>
|
||||
<Bar />
|
||||
</View>
|
||||
</Foo>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<Foo>
|
||||
<View onClick={() => stuff('foo')}>
|
||||
<Bar />
|
||||
</View>
|
||||
</Foo>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{renderItem('foo')}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo === 'foo' && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo['foo'] && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{check('foo') && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo.bar && <Bar />}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<Text>
|
||||
<Trans>{renderItem('foo')}</Trans>
|
||||
</Text>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{null}
|
||||
</View>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<Text>
|
||||
<Trans>{null}</Trans>
|
||||
</Text>
|
||||
`,
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
|
@ -455,6 +619,179 @@ function MyText({ foo }) {
|
|||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{'foo'}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo && 'foo'}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{'foo'}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo && <Trans>{'foo'}</Trans>}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{10}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{10}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{foo + 10}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{\`foo\`}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{\`foo\`}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{foo + \`foo\`}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{_(msg\`foo\`)}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
{foo + _(msg\`foo\`)}
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{_(msg\`foo\`)}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{foo + _(msg\`foo\`)}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>foo</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans><Trans>foo</Trans></Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{foo}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View>
|
||||
<Trans>{'foo'}</Trans>
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
<View prop={
|
||||
<Trans><Text>foo</Text></Trans>
|
||||
}>
|
||||
<Bar />
|
||||
</View>
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ exports.create = function create(context) {
|
|||
const options = context.options[0] || {}
|
||||
const impliedTextProps = options.impliedTextProps ?? []
|
||||
const impliedTextComponents = options.impliedTextComponents ?? []
|
||||
const suggestedTextWrappers = options.suggestedTextWrappers ?? {}
|
||||
const textProps = [...impliedTextProps]
|
||||
const textComponents = ['Text', ...impliedTextComponents]
|
||||
|
||||
|
@ -54,13 +55,13 @@ exports.create = function create(context) {
|
|||
return
|
||||
}
|
||||
if (tagName === 'Trans') {
|
||||
// Skip over it and check above.
|
||||
// Exit and rely on the traversal for <Trans> JSXElement (code below).
|
||||
// TODO: Maybe validate that it's present.
|
||||
parent = parent.parent
|
||||
continue
|
||||
return
|
||||
}
|
||||
let message = 'Wrap this string in <Text>.'
|
||||
if (tagName !== 'View') {
|
||||
const suggestedWrapper = suggestedTextWrappers[tagName]
|
||||
let message = `Wrap this string in <${suggestedWrapper ?? 'Text'}>.`
|
||||
if (tagName !== 'View' && !suggestedWrapper) {
|
||||
message +=
|
||||
' If <' +
|
||||
tagName +
|
||||
|
@ -112,6 +113,189 @@ exports.create = function create(context) {
|
|||
continue
|
||||
}
|
||||
},
|
||||
Literal(node) {
|
||||
if (typeof node.value !== 'string' && typeof node.value !== 'number') {
|
||||
return
|
||||
}
|
||||
let parent = node.parent
|
||||
while (parent) {
|
||||
if (parent.type === 'JSXElement') {
|
||||
const tagName = getTagName(parent)
|
||||
if (isTextComponent(tagName)) {
|
||||
// We're good.
|
||||
return
|
||||
}
|
||||
if (tagName === 'Trans') {
|
||||
// Exit and rely on the traversal for <Trans> JSXElement (code below).
|
||||
// TODO: Maybe validate that it's present.
|
||||
return
|
||||
}
|
||||
const suggestedWrapper = suggestedTextWrappers[tagName]
|
||||
let message = `Wrap this string in <${suggestedWrapper ?? 'Text'}>.`
|
||||
if (tagName !== 'View' && !suggestedWrapper) {
|
||||
message +=
|
||||
' If <' +
|
||||
tagName +
|
||||
'> is guaranteed to render <Text>, ' +
|
||||
'rename it to <' +
|
||||
tagName +
|
||||
'Text> or add it to impliedTextComponents.'
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (parent.type === 'BinaryExpression' && parent.operator === '+') {
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
parent.type === 'JSXExpressionContainer' ||
|
||||
parent.type === 'LogicalExpression'
|
||||
) {
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
|
||||
// Be conservative for other types.
|
||||
return
|
||||
}
|
||||
},
|
||||
TemplateLiteral(node) {
|
||||
let parent = node.parent
|
||||
while (parent) {
|
||||
if (parent.type === 'JSXElement') {
|
||||
const tagName = getTagName(parent)
|
||||
if (isTextComponent(tagName)) {
|
||||
// We're good.
|
||||
return
|
||||
}
|
||||
if (tagName === 'Trans') {
|
||||
// Exit and rely on the traversal for <Trans> JSXElement (code below).
|
||||
// TODO: Maybe validate that it's present.
|
||||
return
|
||||
}
|
||||
const suggestedWrapper = suggestedTextWrappers[tagName]
|
||||
let message = `Wrap this string in <${suggestedWrapper ?? 'Text'}>.`
|
||||
if (tagName !== 'View' && !suggestedWrapper) {
|
||||
message +=
|
||||
' If <' +
|
||||
tagName +
|
||||
'> is guaranteed to render <Text>, ' +
|
||||
'rename it to <' +
|
||||
tagName +
|
||||
'Text> or add it to impliedTextComponents.'
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
parent.type === 'CallExpression' &&
|
||||
parent.callee.type === 'Identifier' &&
|
||||
parent.callee.name === '_'
|
||||
) {
|
||||
// This is a user-facing string, keep going up.
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
|
||||
if (parent.type === 'BinaryExpression' && parent.operator === '+') {
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
parent.type === 'JSXExpressionContainer' ||
|
||||
parent.type === 'LogicalExpression' ||
|
||||
parent.type === 'TaggedTemplateExpression'
|
||||
) {
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
|
||||
// Be conservative for other types.
|
||||
return
|
||||
}
|
||||
},
|
||||
JSXElement(node) {
|
||||
if (getTagName(node) !== 'Trans') {
|
||||
return
|
||||
}
|
||||
let parent = node.parent
|
||||
while (parent) {
|
||||
if (parent.type === 'JSXElement') {
|
||||
const tagName = getTagName(parent)
|
||||
if (isTextComponent(tagName)) {
|
||||
// We're good.
|
||||
return
|
||||
}
|
||||
if (tagName === 'Trans') {
|
||||
// Exit and rely on the traversal for this JSXElement.
|
||||
// TODO: Should nested <Trans> even be allowed?
|
||||
return
|
||||
}
|
||||
const suggestedWrapper = suggestedTextWrappers[tagName]
|
||||
let message = `Wrap this <Trans> in <${suggestedWrapper ?? 'Text'}>.`
|
||||
if (tagName !== 'View' && !suggestedWrapper) {
|
||||
message +=
|
||||
' If <' +
|
||||
tagName +
|
||||
'> is guaranteed to render <Text>, ' +
|
||||
'rename it to <' +
|
||||
tagName +
|
||||
'Text> or add it to impliedTextComponents.'
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
parent.type === 'JSXAttribute' &&
|
||||
parent.name.type === 'JSXIdentifier' &&
|
||||
parent.parent.type === 'JSXOpeningElement' &&
|
||||
parent.parent.parent.type === 'JSXElement'
|
||||
) {
|
||||
const tagName = getTagName(parent.parent.parent)
|
||||
const propName = parent.name.name
|
||||
if (
|
||||
textProps.includes(tagName + ' ' + propName) ||
|
||||
propName === 'text' ||
|
||||
propName.endsWith('Text')
|
||||
) {
|
||||
// We're good.
|
||||
return
|
||||
}
|
||||
const message =
|
||||
'Wrap this <Trans> in <Text>.' +
|
||||
' If `' +
|
||||
propName +
|
||||
'` is guaranteed to be wrapped in <Text>, ' +
|
||||
'rename it to `' +
|
||||
propName +
|
||||
'Text' +
|
||||
'` or add it to impliedTextProps.'
|
||||
context.report({
|
||||
node,
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
parent = parent.parent
|
||||
continue
|
||||
}
|
||||
},
|
||||
ReturnStatement(node) {
|
||||
let fnScope = context.getScope()
|
||||
while (fnScope && fnScope.type !== 'function') {
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import {android, atoms as a, flatten, tokens, useTheme} from '#/alf'
|
||||
import {Props as SVGIconProps} from '#/components/icons/common'
|
||||
|
@ -59,6 +58,10 @@ export type ButtonState = {
|
|||
|
||||
export type ButtonContext = VariantProps & ButtonState
|
||||
|
||||
type NonTextElements =
|
||||
| React.ReactElement
|
||||
| Iterable<React.ReactElement | null | undefined | boolean>
|
||||
|
||||
export type ButtonProps = Pick<
|
||||
PressableProps,
|
||||
'disabled' | 'onPress' | 'testID'
|
||||
|
@ -68,11 +71,9 @@ export type ButtonProps = Pick<
|
|||
testID?: string
|
||||
label: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
children:
|
||||
| React.ReactNode
|
||||
| string
|
||||
| ((context: ButtonContext) => React.ReactNode | string)
|
||||
children: NonTextElements | ((context: ButtonContext) => NonTextElements)
|
||||
}
|
||||
|
||||
export type ButtonTextProps = TextProps & VariantProps & {disabled?: boolean}
|
||||
|
||||
const Context = React.createContext<VariantProps & ButtonState>({
|
||||
|
@ -404,15 +405,7 @@ export function Button({
|
|||
</View>
|
||||
)}
|
||||
<Context.Provider value={context}>
|
||||
{/* @ts-ignore */}
|
||||
{typeof children === 'string' || children?.type === Trans ? (
|
||||
/* @ts-ignore */
|
||||
<ButtonText>{children}</ButtonText>
|
||||
) : typeof children === 'function' ? (
|
||||
children(context)
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{typeof children === 'function' ? children(context) : children}
|
||||
</Context.Provider>
|
||||
</Pressable>
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import {useLingui} from '@lingui/react'
|
|||
import {cleanError} from 'lib/strings/errors'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {Error} from '#/components/Error'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
@ -87,7 +87,9 @@ function ListFooterMaybeError({
|
|||
a.py_sm,
|
||||
]}
|
||||
onPress={onRetry}>
|
||||
<Trans>Retry</Trans>
|
||||
<ButtonText>
|
||||
<Trans>Retry</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -244,7 +244,7 @@ function AppealForm({
|
|||
size="medium"
|
||||
onPress={onPressBack}
|
||||
label={_(msg`Back`)}>
|
||||
{_(msg`Back`)}
|
||||
<ButtonText>{_(msg`Back`)}</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
testID="submitBtn"
|
||||
|
@ -253,7 +253,7 @@ function AppealForm({
|
|||
size="medium"
|
||||
onPress={onSubmit}
|
||||
label={_(msg`Submit`)}>
|
||||
{_(msg`Submit`)}
|
||||
<ButtonText>{_(msg`Submit`)}</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</>
|
||||
|
|
|
@ -10,7 +10,7 @@ import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
|||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {AccountList} from '#/components/AccountList'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {FormContainer} from './FormContainer'
|
||||
|
||||
|
@ -75,7 +75,7 @@ export const ChooseAccountForm = ({
|
|||
color="secondary"
|
||||
size="medium"
|
||||
onPress={onPressBack}>
|
||||
{_(msg`Back`)}
|
||||
<ButtonText>{_(msg`Back`)}</ButtonText>
|
||||
</Button>
|
||||
<View style={[a.flex_1]} />
|
||||
</View>
|
||||
|
|
|
@ -237,7 +237,9 @@ export const LoginForm = ({
|
|||
color="secondary"
|
||||
size="medium"
|
||||
onPress={onPressRetryConnect}>
|
||||
{_(msg`Retry`)}
|
||||
<ButtonText>
|
||||
<Trans>Retry</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
) : !serviceDescription ? (
|
||||
<>
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
useTheme,
|
||||
web,
|
||||
} from '#/alf'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {createPortalGroup} from '#/components/Portal'
|
||||
import {leading, P, Text} from '#/components/Typography'
|
||||
|
@ -73,7 +73,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
|
|||
onPress={() => onboardDispatch({type: 'skip'})}
|
||||
// DEV ONLY
|
||||
label="Clear onboarding state">
|
||||
Clear
|
||||
<ButtonText>Clear</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
|
|
|
@ -167,7 +167,7 @@ export function ServerInputDialog({
|
|||
size="small"
|
||||
onPress={() => control.close()}
|
||||
label={_(msg`Done`)}>
|
||||
{_(msg`Done`)}
|
||||
<ButtonText>{_(msg`Done`)}</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -92,7 +92,9 @@ export function ExportCarDialog({
|
|||
size={gtMobile ? 'small' : 'large'}
|
||||
onPress={() => control.close()}
|
||||
label={_(msg`Done`)}>
|
||||
{_(msg`Done`)}
|
||||
<ButtonText>
|
||||
<Trans>Done</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ import {View} from 'react-native'
|
|||
import {atoms as a} from '#/alf'
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
ButtonColor,
|
||||
ButtonIcon,
|
||||
ButtonText,
|
||||
ButtonVariant,
|
||||
} from '#/components/Button'
|
||||
import {H1} from '#/components/Typography'
|
||||
import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
|
||||
import {H1} from '#/components/Typography'
|
||||
|
||||
export function Buttons() {
|
||||
return (
|
||||
|
@ -29,7 +29,7 @@ export function Buttons() {
|
|||
color={color as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
disabled
|
||||
|
@ -37,7 +37,7 @@ export function Buttons() {
|
|||
color={color as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
@ -54,7 +54,7 @@ export function Buttons() {
|
|||
color={name as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
disabled
|
||||
|
@ -62,7 +62,7 @@ export function Buttons() {
|
|||
color={name as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
),
|
||||
|
@ -77,7 +77,7 @@ export function Buttons() {
|
|||
color={name as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
disabled
|
||||
|
@ -85,7 +85,7 @@ export function Buttons() {
|
|||
color={name as ButtonColor}
|
||||
size="large"
|
||||
label="Click here">
|
||||
Button
|
||||
<ButtonText>Button</ButtonText>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
),
|
||||
|
|
|
@ -3,7 +3,7 @@ import {View} from 'react-native'
|
|||
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {H3, P} from '#/components/Typography'
|
||||
|
@ -26,7 +26,7 @@ export function Dialogs() {
|
|||
basic.open()
|
||||
}}
|
||||
label="Open basic dialog">
|
||||
Open all dialogs
|
||||
<ButtonText>Open all dialogs</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
@ -37,7 +37,7 @@ export function Dialogs() {
|
|||
scrollable.open()
|
||||
}}
|
||||
label="Open basic dialog">
|
||||
Open scrollable dialog
|
||||
<ButtonText>Open scrollable dialog</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
@ -48,7 +48,7 @@ export function Dialogs() {
|
|||
basic.open()
|
||||
}}
|
||||
label="Open basic dialog">
|
||||
Open basic dialog
|
||||
<ButtonText>Open basic dialog</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
@ -57,7 +57,7 @@ export function Dialogs() {
|
|||
size="small"
|
||||
onPress={() => prompt.open()}
|
||||
label="Open prompt">
|
||||
Open prompt
|
||||
<ButtonText>Open prompt</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Prompt.Outer control={prompt}>
|
||||
|
@ -102,7 +102,7 @@ export function Dialogs() {
|
|||
size="small"
|
||||
onPress={closeAllDialogs}
|
||||
label="Close all dialogs">
|
||||
Close all dialogs
|
||||
<ButtonText>Close all dialogs</ButtonText>
|
||||
</Button>
|
||||
<View style={{height: 1000}} />
|
||||
<View style={[a.flex_row, a.justify_end]}>
|
||||
|
@ -116,7 +116,7 @@ export function Dialogs() {
|
|||
})
|
||||
}
|
||||
label="Open basic dialog">
|
||||
Close dialog
|
||||
<ButtonText>Close dialog</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {View} from 'react-native'
|
||||
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {DateField, LabelText} from '#/components/forms/DateField'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
|
@ -191,7 +191,7 @@ export function Forms() {
|
|||
setToggleGroupBValues(['a', 'b'])
|
||||
setToggleGroupCValues(['a'])
|
||||
}}>
|
||||
Reset all toggles
|
||||
<ButtonText>Reset all toggles</ButtonText>
|
||||
</Button>
|
||||
|
||||
<View style={[a.gap_md, a.align_start, a.w_full]}>
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||
|
||||
import {atoms as a, useTheme, ThemeProvider} from '#/alf'
|
||||
import {useSetThemePrefs} from '#/state/shell'
|
||||
import {Button} from '#/components/Button'
|
||||
|
||||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||
import {atoms as a, ThemeProvider, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {Breakpoints} from './Breakpoints'
|
||||
import {Buttons} from './Buttons'
|
||||
import {Dialogs} from './Dialogs'
|
||||
import {Forms} from './Forms'
|
||||
import {Icons} from './Icons'
|
||||
import {Links} from './Links'
|
||||
import {Menus} from './Menus'
|
||||
import {Shadows} from './Shadows'
|
||||
import {Spacing} from './Spacing'
|
||||
import {Theming} from './Theming'
|
||||
import {Typography} from './Typography'
|
||||
import {Spacing} from './Spacing'
|
||||
import {Buttons} from './Buttons'
|
||||
import {Links} from './Links'
|
||||
import {Forms} from './Forms'
|
||||
import {Dialogs} from './Dialogs'
|
||||
import {Breakpoints} from './Breakpoints'
|
||||
import {Shadows} from './Shadows'
|
||||
import {Icons} from './Icons'
|
||||
import {Menus} from './Menus'
|
||||
|
||||
export function Storybook() {
|
||||
const t = useTheme()
|
||||
|
@ -33,7 +32,7 @@ export function Storybook() {
|
|||
size="small"
|
||||
label='Set theme to "system"'
|
||||
onPress={() => setColorMode('system')}>
|
||||
System
|
||||
<ButtonText>System</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
|
@ -41,7 +40,7 @@ export function Storybook() {
|
|||
size="small"
|
||||
label='Set theme to "light"'
|
||||
onPress={() => setColorMode('light')}>
|
||||
Light
|
||||
<ButtonText>Light</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
|
@ -52,7 +51,7 @@ export function Storybook() {
|
|||
setColorMode('dark')
|
||||
setDarkTheme('dim')
|
||||
}}>
|
||||
Dim
|
||||
<ButtonText>Dim</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
|
@ -63,7 +62,7 @@ export function Storybook() {
|
|||
setColorMode('dark')
|
||||
setDarkTheme('dark')
|
||||
}}>
|
||||
Dark
|
||||
<ButtonText>Dark</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
|
|
Loading…
Reference in New Issue