Skip to content

Commit

Permalink
Merge branch 'main' into feat/6298-publish-storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
timwright12 authored Sep 12, 2023
2 parents 0366f26 + 01c530c commit 027668f
Show file tree
Hide file tree
Showing 13 changed files with 2,244 additions and 1,122 deletions.
1 change: 0 additions & 1 deletion packages/components/.env

This file was deleted.

2 changes: 2 additions & 0 deletions packages/components/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/App.tsx
tsconfig.json
Binary file modified packages/components/.yarn/install-state.gz
Binary file not shown.
39 changes: 22 additions & 17 deletions packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@department-of-veterans-affairs/mobile-component-library",
"version": "0.0.13",
"version": "0.0.18",
"description": "VA Design System Mobile Component Library",
"main": "src/index.tsx",
"scripts": {
Expand Down Expand Up @@ -38,29 +38,20 @@
},
"homepage": "https://department-of-veterans-affairs.github.io/va-mobile-temporary",
"dependencies": {
"@expo/webpack-config": "^18.1.2",
"expo": "^49.0.0",
"expo-constants": "~14.4.2",
"expo-font": "^11.4.0",
"expo-linking": "~5.0.2",
"expo-localization": "^14.3.0",
"expo-splash-screen": "~0.20.4",
"expo-status-bar": "~1.6.0",
"@os-team/i18next-react-native-language-detector": "^1.0.28",
"i18next": "^23.4.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-i18next": "^13.0.3",
"react-native": "0.72.3",
"react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "~3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-web": "~0.19.6",
"styled-components": "^6.0.7"
},
"peerDependencies": {
"react": "^18.2.0",
"react-native": "~0.71.7",
"react-native-gesture-handler": "^2.12.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@expo/webpack-config": "^18.1.2",
"@react-native-async-storage/async-storage": "^1.19.0",
"@react-native-community/datetimepicker": "^7.4.0",
"@react-native-community/slider": "^4.4.2",
Expand All @@ -81,9 +72,23 @@
"babel-loader": "^8.3.0",
"babel-plugin-react-docgen-typescript": "^1.5.1",
"babel-plugin-react-native-web": "^0.18.10",
"expo": "^49.0.0",
"expo-constants": "~14.4.2",
"expo-font": "^11.4.0",
"expo-linking": "~5.0.2",
"expo-splash-screen": "~0.20.4",
"expo-status-bar": "~1.6.0",
"gh-pages": "^5.0.0",
"jest": "^29.6.1",
"metro-react-native-babel-preset": "^0.77.0",
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "~0.71.7",
"react-native-gesture-handler": "^2.12.0",
"react-native-reanimated": "~3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-web": "~0.19.6",
"storybook-addon-designs": "^6.3.1",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6"
Expand Down
12 changes: 10 additions & 2 deletions packages/components/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import * as SplashScreen from 'expo-splash-screen'
import { I18nextProvider } from 'react-i18next'
import { View } from 'react-native'
import { registerRootComponent } from 'expo'
import { useCallback } from 'react'
import { useFonts } from 'expo-font'
import React, { useCallback } from 'react'

import i18n from 'utils/translation/i18n'
import i18n from './utils/translation/i18n'

SplashScreen.preventAutoHideAsync()

export const initiateExpo = (expoApp: typeof App) => {
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(expoApp)
}

const App = () => {
// Loads in custom fonts async
const [fontsLoaded, fontError] = useFonts({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,32 @@ export default meta
type Story = StoryObj<SegmentedControlProps>

const statefulComponentRenderer = (props: SegmentedControlProps) => {
const { labels } = props
const [selectedSegment, setSelectedSegment] = useState(labels[0])
const { labels, a11yLabels, a11yHints } = props
const [selectedSegment, setSelectedSegment] = useState(0)

return (
<SegmentedControl
labels={labels}
onChange={setSelectedSegment}
selected={labels.indexOf(selectedSegment)}
selected={selectedSegment}
a11yLabels={a11yLabels}
a11yHints={a11yHints}
/>
)
}

export const twoSegments: Story = {
render: statefulComponentRenderer,
args: {
labels: ['Inbox (4)', 'Folders'],
labels: ['Inbox (3)', 'Folders'],
a11yLabels: ['Inbox'],
a11yHints: [
'You have 3 unread messages. Review messages in your inbox',
'Review your folders',
],
},
parameters: {
design: [
{
name: 'Figma header',
type: 'figma',
url: 'https://www.figma.com/file/QVLPB3eOunmKrgQOuOt0SU/%F0%9F%93%90-DesignLibrary2.0---VAMobile?type=design&node-id=7327%3A3032&mode=design&t=F0hXXm33PlK48vBm-1',
},
{
name: 'Figma master component',
type: 'figma',
url: 'https://www.figma.com/file/QVLPB3eOunmKrgQOuOt0SU/%F0%9F%93%90-DesignLibrary2.0---VAMobile?type=design&node-id=7332%3A11264&mode=design&t=IfpGfogEOoBtNhmN-1',
},
{
name: 'Figma component overview',
type: 'figma',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ import { useTranslation } from 'react-i18next'
import React, { FC, useEffect } from 'react'
import styled from 'styled-components'

import { ComponentWrapper } from '../../wrapper'

/**
* Props for {@link SegmentedControl}
*/
export type SegmentedControlProps = {
/** Array of segment labels */
labels: string[]
/** Handler function for changing segment selection */
onChange: (selection: string) => void
onChange: (selection: number) => void
/** Index of the currently selected segment */
selected: number
/** Optional list of accessibility hints for labels */
labelsA11yHints?: string[]
/** Optional array of segment accessibility override labels */
a11yLabels?: string[]
/** Optional array of segment accessibility hints */
a11yHints?: string[]
}

type SegmentProps = {
Expand All @@ -45,11 +49,16 @@ export const SegmentedControl: FC<SegmentedControlProps> = ({
labels,
onChange,
selected,
labelsA11yHints: accessibilityHints,
a11yLabels,
a11yHints,
}) => {
const { t } = useTranslation()
const colorScheme = useColorScheme()

useEffect(() => {
onChange(selected)
}, [selected, onChange, labels])

// Copied from DSVA color tokens in css-library
const colorTokens = {
white: '#FFFFFF',
Expand All @@ -73,10 +82,6 @@ export const SegmentedControl: FC<SegmentedControlProps> = ({
inactiveBgColor = colorTokens.gray.dark
}

useEffect(() => {
onChange(labels[selected])
}, [selected, onChange, labels])

const viewStyle: ViewStyle = {
alignSelf: 'baseline',
backgroundColor: inactiveBgColor,
Expand All @@ -87,49 +92,61 @@ export const SegmentedControl: FC<SegmentedControlProps> = ({
padding: 2,
}

return (
<View style={viewStyle} accessibilityRole="tablist">
{labels.map((label, index) => {
const isSelected = selected === index
/**
* Function to build individual segment within controller
* @param label - Segment label
* @param index - Segment index position in array
*/
const buildSegment = (label: string, index: number) => {
const isSelected = selected === index

const accessibilityLabel = a11yLabels
? a11yLabels[index] || labels[index]
: labels[index]
const accessibilityValue = {
text: t('listPosition', {
position: index + 1,
total: labels.length,
}),
}

const font: TextStyle = {
fontFamily: isSelected
? 'SourceSansPro-Bold'
: 'SourceSansPro-Regular',
fontSize: 20,
lineHeight: 30,
}
// TODO: Replace with typography tokens
const font: TextStyle = {
fontFamily: isSelected ? 'SourceSansPro-Bold' : 'SourceSansPro-Regular',
fontSize: 20,
lineHeight: 30,
}

const textStyle: TextStyle = {
...font,
color: textColor,
textAlign: 'center',
}
const textStyle: TextStyle = {
...font,
color: textColor,
textAlign: 'center',
}

return (
<Segment
onPress={(): void => onChange(labels[index])}
backgroundColor={isSelected ? activeBgColor : inactiveBgColor}
isSelected={isSelected}
key={index}
widthPct={`${100 / labels.length}%`}
accessibilityHint={
accessibilityHints ? accessibilityHints[index] : ''
}
accessibilityValue={{
text: t('listPosition', {
position: index + 1,
total: labels.length,
}),
}}
accessibilityRole={'tab'}
accessibilityState={{ selected: isSelected }}>
<Text allowFontScaling={false} style={textStyle}>
{label}
</Text>
</Segment>
)
})}
</View>
return (
<Segment
onPress={(): void => onChange(index)}
backgroundColor={isSelected ? activeBgColor : inactiveBgColor}
isSelected={isSelected}
key={index}
widthPct={`${100 / labels.length}%`}
accessibilityLabel={accessibilityLabel}
accessibilityHint={a11yHints ? a11yHints[index] : ''}
accessibilityValue={accessibilityValue}
accessibilityRole={'tab'}
accessibilityState={{ selected: isSelected }}>
<Text allowFontScaling={false} style={textStyle}>
{label}
</Text>
</Segment>
)
}

return (
<ComponentWrapper>
<View style={viewStyle} accessibilityRole="tablist">
{labels.map((label, index) => buildSegment(label, index))}
</View>
</ComponentWrapper>
)
}
19 changes: 6 additions & 13 deletions packages/components/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
// Import components here so they can be exported through npm
import { registerRootComponent } from 'expo'
import App from './main'

import App from './App'
const expoApp = App.default

const storybookEnabled = process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true'

if (storybookEnabled) {
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App)
if (expoApp && App.initiateExpo) {
App.initiateExpo(expoApp)
}


export { MyButton } from 'components/Button/Button'
export { SegmentedControl } from 'components/SegmentedControl/SegmentedControl'
// Export components here so they are exported through npm
export { SegmentedControl } from './components/SegmentedControl/SegmentedControl'
17 changes: 17 additions & 0 deletions packages/components/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Module to set App environment:
* - Standalone Expo App (Storybook environment)
* - Component wrapper environment as part of NPM package/within external app (App = null)
*/
export type AppType = {
default: JSX.Element | null
initiateExpo: ((expoApp: JSX.Element | null) => void) | null
}

let App: AppType
try {
App = require('./App.tsx')
} catch {
App = require('./wrapper.tsx')
}
export default App
37 changes: 18 additions & 19 deletions packages/components/src/utils/translation/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import * as RNLocalize from 'expo-localization'
import { initReactI18next } from 'react-i18next'
import RNLanguageDetector from '@os-team/i18next-react-native-language-detector'
import i18n from 'i18next'

import * as enTranslation from './en.json'

const fallbackLanguage = { languageTag: 'en', isRTL: false }
const defaultLanguage = RNLocalize.getLocales()[0] || fallbackLanguage

export const resources = {
en: { translation: enTranslation },
}

// Initialize the internationalization library
i18n.use(initReactI18next).init({
lng: defaultLanguage.languageTag,
resources,
keySeparator: false,
fallbackLng: 'en',
compatibilityJSON: 'v3',
debug: true,
interpolation: {
escapeValue: false,
formatSeparator: ',',
},
react: {
useSuspense: true,
},
})
i18n
.use(RNLanguageDetector)
.use(initReactI18next)
.init({
resources,
keySeparator: false,
fallbackLng: 'en',
compatibilityJSON: 'v3',
debug: true,
interpolation: {
escapeValue: false,
formatSeparator: ',',
},
react: {
useSuspense: true,
},
})

export default i18n
Loading

0 comments on commit 027668f

Please sign in to comment.