Skip to content

Commit

Permalink
Add sample page
Browse files Browse the repository at this point in the history
Add hook.
Add local state.
Add global state with duck and selectors.
Add context state with context and hook.
  • Loading branch information
matthewwalsh0 committed Jan 24, 2025
1 parent d3d81ad commit 2c386a7
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 1 deletion.
95 changes: 95 additions & 0 deletions ui/components/app/sample/sample-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useCallback, useState } from 'react';
import {
AlignItems,
BackgroundColor,
IconColor,
TextAlign,
TextVariant,
} from '../../../helpers/constants/design-system';
import {
Button,
ButtonIcon,
ButtonIconSize,
IconName,
Text,
} from '../../component-library';
import { Content, Header, Page } from '../../multichain/pages/page';
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { useHistory } from 'react-router-dom';

Check failure on line 18 in ui/components/app/sample/sample-page.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

`react-router-dom` import should occur before import of `../../../helpers/constants/design-system`
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useSample } from '../../../hooks/sample/useSample';
import { useSampleContext } from '../../../hooks/sample/useSampleContext';

export function SamplePage() {
const t = useI18nContext();
const history = useHistory();
const [localState, setLocalState] = useState<number>(0);
const { globalCounter, updateGlobalCounter } = useSample();

const { counter: contextCounter, updateCounter: updateContextCounter } =
useSampleContext();

const handleLocalStateClick = useCallback(() => {
setLocalState((prev) => prev + 1);
}, [setLocalState]);

const handleGlobalStateClick = useCallback(() => {
updateGlobalCounter(1);
}, [updateGlobalCounter]);

const handleContextStateClick = useCallback(() => {
updateContextCounter(1);
}, [updateContextCounter]);

return (
<Page className="main-container" data-testid="sample-page">
<Header
backgroundColor={BackgroundColor.backgroundDefault}
startAccessory={
<ButtonIcon
ariaLabel={t('back')}
iconName={IconName.ArrowLeft}
className="connections-header__start-accessory"
color={IconColor.iconDefault}
onClick={() => history.push(DEFAULT_ROUTE)}
size={ButtonIconSize.Sm}
/>
}
>
<Text
as="span"
variant={TextVariant.headingMd}
textAlign={TextAlign.Center}
>
Sample Page
</Text>
</Header>
<Content alignItems={AlignItems.center} gap={2}>
<Text marginBottom={1}>{`Local React State: ${localState}`}</Text>
<Button
onClick={handleLocalStateClick}
textAlign={TextAlign.Center}
marginBottom={6}
>
Update Local State
</Button>
<Text marginBottom={1}>{`Global Redux State: ${globalCounter}`}</Text>
<Button
onClick={handleGlobalStateClick}
textAlign={TextAlign.Center}
marginBottom={6}
>
Update Redux State
</Button>
<Text marginBottom={1}>{`React Context State: ${contextCounter}`}</Text>
<Button
onClick={handleContextStateClick}
textAlign={TextAlign.Center}
marginBottom={6}
>
Update Context State
</Button>
</Content>
</Page>
);
}
26 changes: 26 additions & 0 deletions ui/components/app/wallet-overview/coin-buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import { getCurrentChainId } from '../../../../shared/modules/selectors/networks
///: BEGIN:ONLY_INCLUDE_IF(solana-swaps)
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
///: END:ONLY_INCLUDE_IF
import { useSample } from '../../../hooks/sample/useSample';

type CoinButtonsProps = {
account: InternalAccount;
Expand Down Expand Up @@ -490,6 +491,12 @@ const CoinButtons = ({
///: END:ONLY_INCLUDE_IF
]);

const { openSampleFeature } = useSample();

const handleSampleClick = useCallback(() => {
openSampleFeature();
}, [openSampleFeature]);

return (
<Box display={Display.Flex} justifyContent={JustifyContent.spaceEvenly}>
{
Expand Down Expand Up @@ -615,6 +622,25 @@ const CoinButtons = ({
/>
</>
}

<IconButton
className={`${classPrefix}-overview__button`}
iconButtonClassName={iconButtonClassName}
disabled={false}
Icon={
<Icon
name={IconName.Gas}
color={IconColor.primaryInverse}
size={IconSize.Sm}
/>
}
onClick={handleSampleClick}
label="Sample"
data-testid="token-overview-button-sample"
tooltipRender={(contents: React.ReactElement) =>
generateTooltip('swapButton', contents)
}
/>
</Box>
);
};
Expand Down
4 changes: 4 additions & 0 deletions ui/components/app/wallet-overview/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@
border-radius: 18px;
margin-top: 6px;
}

&__button {
width: 55px !important;
}
}

.token-overview {
Expand Down
2 changes: 2 additions & 0 deletions ui/ducks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import bridgeReducer from './bridge/bridge';
import historyReducer from './history/history';
import rampsReducer from './ramps/ramps';
import confirmAlertsReducer from './confirm-alerts/confirm-alerts';
import sampleReducer from './sample/sample';

export default combineReducers({
[AlertTypes.invalidCustomNetwork]: invalidCustomNetwork,
Expand All @@ -30,4 +31,5 @@ export default combineReducers({
bridge: bridgeReducer,
gas: gasReducer,
localeMessages: localeMessagesReducer,
sample: sampleReducer,
});
38 changes: 38 additions & 0 deletions ui/ducks/sample/sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export type SampleGlobalState = {
counter: number;
};

type UpdateSampleCounterAction = {
type: 'UPDATE_SAMPLE_COUNTER';
amount: number;
};

type Action = UpdateSampleCounterAction;

const INIT_STATE: SampleGlobalState = {
counter: 0,
};

export default function sampleReducer(
// eslint-disable-next-line @typescript-eslint/default-param-last
state: SampleGlobalState = INIT_STATE,
action: Action,
) {
switch (action.type) {
case 'UPDATE_SAMPLE_COUNTER':
return {
...state,
counter: state.counter + action.amount,
};

default:
return state;
}
}

export function updateSampleCounter(amount: number): UpdateSampleCounterAction {
return {
type: 'UPDATE_SAMPLE_COUNTER',
amount,
};
}
2 changes: 2 additions & 0 deletions ui/helpers/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,5 @@ export const ONBOARDING_METAMETRICS = '/onboarding/metametrics';
export const INITIALIZE_EXPERIMENTAL_AREA = '/initialize/experimental-area';
export const ONBOARDING_EXPERIMENTAL_AREA = '/onboarding/experimental-area';
///: END:ONLY_INCLUDE_IF

export const SAMPLE_ROUTE = '/sample';
25 changes: 25 additions & 0 deletions ui/hooks/sample/useSample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCallback } from 'react';
import { SAMPLE_ROUTE } from '../../helpers/constants/routes';

Check failure on line 2 in ui/hooks/sample/useSample.ts

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

`../../helpers/constants/routes` import should occur after import of `react-redux`
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { selectSampleCounter } from '../../selectors/sample';
import { updateSampleCounter } from '../../ducks/sample/sample';

export function useSample() {
const history = useHistory();
const dispatch = useDispatch();
const globalCounter = useSelector(selectSampleCounter);

const updateGlobalCounter = useCallback(
(amount: number) => {
dispatch(updateSampleCounter(amount));
},
[dispatch],
);

const openSampleFeature = useCallback(() => {
history.push(SAMPLE_ROUTE);
}, [history]);

return { globalCounter, openSampleFeature, updateGlobalCounter };
}
48 changes: 48 additions & 0 deletions ui/hooks/sample/useSampleContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, {
ReactElement,
createContext,
useCallback,
useContext,
useMemo,
useState,
} from 'react';

export type SampleContexType = {
counter: number;
updateCounter: (amount: number) => void;
};

export const SampleContext = createContext<SampleContexType | undefined>(
undefined,
);

export function SampleContextProvider({ children }: { children: ReactElement }) {

Check failure on line 19 in ui/hooks/sample/useSampleContext.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Replace `·children·}:·{·children:·ReactElement·` with `⏎··children,⏎}:·{⏎··children:·ReactElement;⏎`
const [counter, setCounter] = useState(0);

const updateCounter = useCallback((amount: number) => {
setCounter((prevCounter) => prevCounter + amount);
}, []);

const value = useMemo(
() => ({
counter,
updateCounter,
}),
[counter],
);

return <SampleContext.Provider value={value}>{children}</SampleContext.Provider>

Check failure on line 34 in ui/hooks/sample/useSampleContext.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Insert `(⏎····`

Check failure on line 35 in ui/hooks/sample/useSampleContext.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Insert `··);`
};

Check failure on line 36 in ui/hooks/sample/useSampleContext.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Delete `;`

export function useSampleContext(): SampleContexType {
const context = useContext(SampleContext);

if (!context) {
throw new Error(
'useSampleContext must be used within a SampleContextProvider',
);
}

return context;
};

Check failure on line 48 in ui/hooks/sample/useSampleContext.tsx

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Delete `;`
5 changes: 4 additions & 1 deletion ui/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { MetamaskNotificationsProvider } from '../contexts/metamask-notifications';
import { AssetPollingProvider } from '../contexts/assetPolling';
import { MetamaskIdentityProvider } from '../contexts/identity';
import { SampleContextProvider } from '../hooks/sample/useSampleContext';
import ErrorPage from './error-page/error-page.component';

import Routes from './routes';
Expand Down Expand Up @@ -54,7 +55,9 @@ class Index extends PureComponent {
<AssetPollingProvider>
<MetamaskIdentityProvider>
<MetamaskNotificationsProvider>
<Routes />
<SampleContextProvider>
<Routes />
</SampleContextProvider>
</MetamaskNotificationsProvider>
</MetamaskIdentityProvider>
</AssetPollingProvider>
Expand Down
3 changes: 3 additions & 0 deletions ui/pages/routes/routes.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
NOTIFICATIONS_SETTINGS_ROUTE,
CROSS_CHAIN_SWAP_ROUTE,
CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE,
SAMPLE_ROUTE,
} from '../../helpers/constants/routes';

import {
Expand Down Expand Up @@ -93,6 +94,7 @@ import {
isCorrectDeveloperTransactionType,
isCorrectSignatureApprovalType,
} from '../../../shared/lib/confirmation.utils';
import { SamplePage } from '../../components/app/sample/sample-page';
import {
getConnectingLabel,
hideAppHeader,
Expand Down Expand Up @@ -405,6 +407,7 @@ export default class Routes extends Component {
component={ReviewPermissions}
exact
/>
<Authenticated path={SAMPLE_ROUTE} component={SamplePage} />
<Authenticated path={DEFAULT_ROUTE} component={Home} />
</Switch>
</Suspense>
Expand Down
15 changes: 15 additions & 0 deletions ui/selectors/sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSelector } from 'reselect';
import { SampleGlobalState } from '../ducks/sample/sample';

type State = {
sample: SampleGlobalState;
};

export function selectSample(state: State): SampleGlobalState {
return state.sample;
}

export const selectSampleCounter = createSelector(
selectSample,
(sample) => sample.counter,
);

0 comments on commit 2c386a7

Please sign in to comment.