recoil#useRecoilCallback TypeScript Examples
The following examples show how to use
recoil#useRecoilCallback.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: RecoilNexus.tsx From recoil-nexus with MIT License | 6 votes |
export default function RecoilNexus() {
nexus.get = useRecoilCallback<[atom: RecoilValue<any>], any>(({ snapshot }) =>
function <T>(atom: RecoilValue<T>) {
return snapshot.getLoadable(atom).contents
}, [])
nexus.getPromise = useRecoilCallback<[atom: RecoilValue<any>], Promise<any>>(({ snapshot }) =>
function <T>(atom: RecoilValue<T>) {
return snapshot.getPromise(atom)
}, [])
nexus.set = useRecoilCallback(({ set }) => set, [])
nexus.reset = useRecoilCallback(({ reset }) => reset, [])
return null
}
Example #2
Source File: useExportResults.ts From nextclade with MIT License | 6 votes |
function useResultsExport(exportFn: (filename: string, snapshot: Snapshot, worker: ExportWorker) => Promise<void>) {
return useRecoilCallback(
({ set, snapshot }) => {
const snapshotRelease = snapshot.retain()
return (filename: string) => {
void ExportWorker.get()
.then((worker) => exportFn(filename, snapshot, worker))
.catch((error) => {
set(globalErrorAtom, error)
})
.finally(() => {
snapshotRelease()
})
}
},
[exportFn],
)
}
Example #3
Source File: SequenceSelector.tsx From nextclade with MIT License | 5 votes |
export function SequenceSelector() {
const { t } = useTranslationSafe()
const geneNames = useRecoilValue(geneNamesAtom)
const viewedGene = useRecoilValue(viewedGeneAtom)
const onChangeGene = useRecoilCallback(
({ set }) =>
(e: React.ChangeEvent<HTMLSelectElement>) => {
set(viewedGeneAtom, e.target.value)
},
[],
)
const getOptionText = useCallback(
(gene: string) => {
if (gene === GENE_OPTION_NUC_SEQUENCE) {
return t('Nucleotide sequence')
}
return t('Gene {{geneName}}', { geneName: gene })
},
[t],
)
const geneOptions = useMemo(() => {
return [GENE_OPTION_NUC_SEQUENCE, ...geneNames].map((gene) => {
return (
<option key={gene} value={gene}>
{getOptionText(gene)}
</option>
)
})
}, [geneNames, getOptionText])
return (
<Select
name="sequence-view-gene-dropdown"
id="sequence-view-gene-dropdown"
onChange={onChangeGene}
value={viewedGene}
>
{geneOptions}
</Select>
)
}
Example #4
Source File: useRunAnalysis.ts From nextclade with MIT License | 5 votes |
export function useRunAnalysis() {
const router = useRouter()
const dispatch = useDispatch()
return useRecoilCallback(
({ set, reset, snapshot: { getPromise } }) =>
() => {
set(analysisStatusGlobalAtom, AlgorithmGlobalStatus.loadingData)
set(showNewRunPopupAtom, false)
reset(analysisResultsAtom)
const numThreads = getPromise(numThreadsAtom)
const datasetCurrent = getPromise(datasetCurrentAtom)
const qrySeq = getPromise(qrySeqInputAtom)
const inputs: LaunchAnalysisInputs = {
ref_seq_str: getPromise(refSeqInputAtom),
gene_map_str: getPromise(geneMapInputAtom),
tree_str: getPromise(refTreeInputAtom),
qc_config_str: getPromise(qcConfigInputAtom),
virus_properties_str: getPromise(virusPropertiesInputAtom),
pcr_primers_str: getPromise(primersCsvInputAtom),
}
const callbacks: LaunchAnalysisCallbacks = {
onGlobalStatus(status) {
set(analysisStatusGlobalAtom, status)
},
onInitialData({ geneMap, genomeSize, cladeNodeAttrKeyDescs }) {
set(geneMapAtom, geneMap)
set(genomeSizeAtom, genomeSize)
set(cladeNodeAttrDescsAtom, cladeNodeAttrKeyDescs)
},
onParsedFasta(/* record */) {
// TODO: this does not work well: updates in `onAnalysisResult()` callback below fight with this one.
// Figure out how to make them both work.
// set(analysisResultsAtom(record.seqName), { index: record.index, seqName: record.seqName })
},
onAnalysisResult(result) {
set(analysisResultAtom(result.seqName), result)
},
onError(error) {
set(globalErrorAtom, error)
},
onTree(tree: AuspiceJsonV2) {
set(treeAtom, tree)
const auspiceState = createAuspiceState(tree, dispatch)
dispatch(auspiceStartClean(auspiceState))
dispatch(changeColorBy())
dispatch(treeFilterByNodeType(['New']))
},
onComplete() {},
}
router
.push('/results', '/results')
.then(async () => {
set(analysisStatusGlobalAtom, AlgorithmGlobalStatus.initWorkers)
return launchAnalysis(qrySeq, inputs, callbacks, datasetCurrent, numThreads)
})
.catch((error) => {
set(analysisStatusGlobalAtom, AlgorithmGlobalStatus.failed)
set(globalErrorAtom, sanitizeError(error))
})
},
[router, dispatch],
)
}
Example #5
Source File: _app.tsx From nextclade with MIT License | 4 votes |
/**
* Dummy component that allows to set recoil state asynchronously. Needed because RecoilRoot's initializeState
* currently only handles synchronous update and any calls to set() from promises have no effect
*/
export function RecoilStateInitializer() {
const router = useRouter()
// NOTE: Do manual parsing, because router.query is randomly empty on the first few renders and on repeated renders.
// This is important, because various states depend on query, and when it changes back and forth,
// the state also changes unexpectedly.
const { query: urlQuery } = useMemo(() => parseUrl(router.asPath), [router.asPath])
const [initialized, setInitialized] = useRecoilState(isInitializedAtom)
const run = useRunAnalysis()
const error = useRecoilValue(globalErrorAtom)
const initialize = useRecoilCallback(({ set, snapshot }) => () => {
if (initialized) {
return
}
const snapShotRelease = snapshot.retain()
const { getPromise } = snapshot
// eslint-disable-next-line no-void
void initializeDatasets(urlQuery)
.catch((error) => {
// Dataset error is fatal and we want error to be handled in the ErrorBoundary
setInitialized(false)
throw error
})
.then(({ datasets, defaultDatasetName, defaultDatasetNameFriendly, currentDatasetName }) => {
set(datasetsAtom, {
datasets,
defaultDatasetName,
defaultDatasetNameFriendly,
})
set(datasetCurrentNameAtom, (previous) => currentDatasetName ?? previous)
return undefined
})
.then(() => {
const qrySeqInput = createInputFromUrlParamMaybe(urlQuery, 'input-fasta')
set(qrySeqInputAtom, qrySeqInput)
set(refSeqInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-root-seq'))
set(geneMapInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-gene-map'))
set(refTreeInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-tree'))
set(qcConfigInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-qc-config'))
set(virusPropertiesInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-pcr-primers'))
set(primersCsvInputAtom, createInputFromUrlParamMaybe(urlQuery, 'input-virus-properties'))
if (qrySeqInput) {
run()
}
return undefined
})
.then(async () => {
const changelogShouldShowOnUpdates = await getPromise(changelogShouldShowOnUpdatesAtom)
const changelogLastVersionSeen = await getPromise(changelogLastVersionSeenAtom)
set(changelogIsShownAtom, shouldShowChangelog(changelogLastVersionSeen, changelogShouldShowOnUpdates))
set(changelogLastVersionSeenAtom, (prev) => process.env.PACKAGE_VERSION ?? prev ?? '')
return undefined
})
.then(() => {
setInitialized(true)
return undefined
})
.catch((error) => {
setInitialized(true)
set(globalErrorAtom, sanitizeError(error))
})
.finally(() => {
snapShotRelease()
})
})
useEffect(() => {
initialize()
})
if (!initialized && !isNil(error)) {
throw error
}
return null
}
Example #6
Source File: index.spec.tsx From recoil-persist with MIT License | 4 votes |
function testPersistWith(storage: TestableStorage) {
describe(`Storage: ${storage.name}`, () => {
const testKey = 'test-key'
const { persistAtom } = recoilPersist({ key: testKey, storage })
const getStateValue = () => {
const value = storage.getState()[testKey]
if (value == undefined) {
return {}
}
return JSON.parse(value)
}
const getAtomKey = (key: string) => {
return `${storage.name}_${key}`
}
const counterState = atom({
key: getAtomKey('count'),
default: 0,
effects_UNSTABLE: [persistAtom],
})
const counterFamily = atomFamily({
key: getAtomKey('countFamily'),
default: 0,
effects_UNSTABLE: [persistAtom],
})
const counterState4 = atom({
key: getAtomKey('count4'),
default: 0,
})
function Demo() {
const [count, setCount] = useRecoilState(counterState)
const [count2, setCount2] = useRecoilState(counterFamily('2'))
const [count3, setCount3] = useRecoilState(counterFamily('3'))
const [count4, setCount4] = useRecoilState(counterState4)
const resetCounter3 = useResetRecoilState(counterFamily('3'))
const updateMultiple = useRecoilCallback(({ set }) => () => {
set(counterState, 10)
set(counterFamily('2'), 10)
})
return (
<div>
<p data-testid="count-value">{count}</p>
<p data-testid="count2-value">{count2}</p>
<p data-testid="count3-value">{count3}</p>
<p data-testid="count4-value">{count4}</p>
<button
data-testid="count-increase"
onClick={() => setCount(count + 1)}
>
Increase
</button>
<button
data-testid="count2-increase"
onClick={() => setCount2(count2 + 1)}
>
Increase 2
</button>
<button
data-testid="count3-increase"
onClick={() => setCount3(count3 + 1)}
>
Increase 3
</button>
<button
data-testid="count4-increase"
onClick={() => setCount4(count4 + 1)}
>
Increase 4
</button>
<button
data-testid="count3-null-value"
onClick={() => setCount3(null)}
>
Set value to null
</button>
<button
data-testid="count3-undefined-value"
onClick={() => setCount3(undefined)}
>
Set value to undefined
</button>
<button data-testid="count3-reset" onClick={() => resetCounter3()}>
Reset count 3
</button>
<button
data-testid="update-multiple"
onClick={() => updateMultiple()}
>
Update multiple
</button>
</div>
)
}
beforeEach(() => {
console.error = jest.fn()
})
afterEach(() => {
storage.clear()
jest.restoreAllMocks()
})
it('should be removed from storage on reset', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count3-increase'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe('1'),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('countFamily__"3"')]: 1,
})
fireEvent.click(getByTestId('count3-reset'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe('0'),
)
expect(getStateValue()).toStrictEqual({})
})
it('should handle reset atom with default value', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count3-reset'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe('0'),
)
expect(getStateValue()).toStrictEqual({})
})
it('should update storage with null', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count3-null-value'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe(''),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('countFamily__"3"')]: null,
})
})
it('should update storage with undefined', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count3-undefined-value'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe(''),
)
expect(getStateValue()).toStrictEqual({})
})
it('should update storage', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count-increase'))
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('1'),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('count')]: 1,
})
})
it('should update storage if using atomFamily', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count2-increase'))
await waitFor(() =>
expect(getByTestId('count2-value').innerHTML).toBe('1'),
)
fireEvent.click(getByTestId('count3-increase'))
await waitFor(() =>
expect(getByTestId('count3-value').innerHTML).toBe('1'),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('countFamily__"2"')]: 1,
[getAtomKey('countFamily__"3"')]: 1,
})
})
it('should not persist atom with no effect', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count4-increase'))
await waitFor(() =>
expect(getByTestId('count4-value').innerHTML).toBe('1'),
)
expect(storage.getState()[testKey]).toBeUndefined()
})
it('should read state from storage', async () => {
await storage.setItem(
testKey,
JSON.stringify({
[getAtomKey('count')]: 1,
[getAtomKey('countFamily__"2"')]: 1,
}),
)
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('1'),
)
await waitFor(() =>
expect(getByTestId('count2-value').innerHTML).toBe('1'),
)
})
it('should use default value if not in storage', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
expect(getByTestId('count3-value').innerHTML).toBe('0')
})
it('should handle non jsonable object in storage', async () => {
storage.setItem(testKey, 'test string')
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count-increase'))
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('1'),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('count')]: 1,
})
})
it('should handle non jsonable object in state', async () => {
let mock = jest.spyOn(JSON, 'stringify').mockImplementation(() => {
throw Error('mock error')
})
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('count-increase'))
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('1'),
)
expect(mock).toHaveBeenCalledTimes(1)
expect(console.error).toHaveBeenCalledTimes(1)
})
it('should handle non-existent atom name stored in storage', async () => {
storage.setItem(
testKey,
JSON.stringify({
notExist: 'test value',
}),
)
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('0'),
)
})
it.skip('should handle updating multiple atomes', async () => {
const { getByTestId } = render(
<RecoilRoot>
<Demo />
</RecoilRoot>,
)
fireEvent.click(getByTestId('update-multiple'))
await waitFor(() =>
expect(getByTestId('count-value').innerHTML).toBe('10'),
)
await waitFor(() =>
expect(getByTestId('count2-value').innerHTML).toBe('10'),
)
expect(getStateValue()).toStrictEqual({
[getAtomKey('count')]: 10,
[getAtomKey('countFamily__"2"')]: 10,
})
})
})
}
Example #7
Source File: ResultsTable.tsx From nextclade with MIT License | 3 votes |
export function ResultsTable() {
const { t } = useTranslation()
const seqNamesImmediate = useRecoilValue(seqNamesFilteredAtom)
const seqNames = useDeferredValue(seqNamesImmediate)
const columnWidthsPx = useRecoilValue(resultsTableColumnWidthsPxAtom)
const dynamicColumnWidthPx = useRecoilValue(resultsTableDynamicColumnWidthPxAtom)
const cladeNodeAttrKeys = useRecoilValue(cladeNodeAttrKeysAtom)
const cladeNodeAttrDescs = useRecoilValue(cladeNodeAttrDescsAtom)
const isResultsFilterPanelCollapsed = useRecoilValue(isResultsFilterPanelCollapsedAtom)
const viewedGene = useRecoilValue(viewedGeneAtom)
const rowData: TableRowDatum[] = useMemo(() => {
return seqNames.map((seqName) => ({
seqName,
viewedGene,
columnWidthsPx,
dynamicColumnWidthPx,
cladeNodeAttrKeys,
}))
}, [cladeNodeAttrKeys, columnWidthsPx, dynamicColumnWidthPx, seqNames, viewedGene])
// TODO: we could use a map (object) and refer to filters by name,
// in order to reduce code duplication in the state, callbacks and components being rendered
const sortByIndexAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.index, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByIndexDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.index, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByNameAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.seqName, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByNameDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.seqName, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByQcIssuesAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.qcIssues, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByQcIssuesDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.qcIssues, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByCladeAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.clade, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByCladeDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.clade, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalMutationsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalMutations, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalMutationsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalMutations, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalNonAcgtnAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalNonACGTNs, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalNonAcgtnDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalNonACGTNs, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalNsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalMissing, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalNsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalMissing, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalGapsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalGaps, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalGapsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalGaps, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalInsertionsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalInsertions, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalInsertionsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalInsertions, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalFrameShiftsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalFrameShifts, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalFrameShiftsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalFrameShifts, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByTotalStopCodonsAsc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalStopCodons, direction: SortDirection.asc }), undefined) }, []) // prettier-ignore
const sortByTotalStopCodonsDesc = useRecoilCallback(({set}) => () => { set(sortAnalysisResultsAtom({ category: SortCategory.totalStopCodons, direction: SortDirection.desc }), undefined) }, []) // prettier-ignore
const sortByKey = useRecoilCallback(({ set }) => (key: string, direction: SortDirection) => () => {
set(sortAnalysisResultsByKeyAtom({ key, direction }), undefined)
}, []) // prettier-ignore
const dynamicColumns = useMemo(() => {
return cladeNodeAttrDescs.map(({ name: attrKey, displayName, description }) => {
const sortAsc = sortByKey(attrKey, SortDirection.asc)
const sortDesc = sortByKey(attrKey, SortDirection.desc)
return (
<TableHeaderCell key={attrKey} basis={dynamicColumnWidthPx} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{displayName}</TableCellText>
<ResultsControlsSort sortAsc={sortAsc} sortDesc={sortDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-clade" wide>
<h5>{`Column: ${displayName}`}</h5>
<p>{description}</p>
</ButtonHelpStyled>
</TableHeaderCell>
)
})
}, [cladeNodeAttrDescs, dynamicColumnWidthPx, sortByKey])
return (
<Table rounded={isResultsFilterPanelCollapsed}>
<TableHeaderRow>
<TableHeaderCell first basis={columnWidthsPx.id} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('ID')}</TableCellText>
<ResultsControlsSort sortAsc={sortByIndexAsc} sortDesc={sortByIndexDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-seq-id">
<HelpTipsColumnId />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.seqName} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Sequence name')}</TableCellText>
<ResultsControlsSort sortAsc={sortByNameAsc} sortDesc={sortByNameDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-seq-name">
<HelpTipsColumnSeqName />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.qc} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('QC')}</TableCellText>
<ResultsControlsSort sortAsc={sortByQcIssuesAsc} sortDesc={sortByQcIssuesDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-qc">
<HelpTipsColumnQC />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.clade} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Clade')}</TableCellText>
<ResultsControlsSort sortAsc={sortByCladeAsc} sortDesc={sortByCladeDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-clade" wide>
<HelpTipsColumnClade />
</ButtonHelpStyled>
</TableHeaderCell>
{dynamicColumns}
<TableHeaderCell basis={columnWidthsPx.mut} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Mut.')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalMutationsAsc} sortDesc={sortByTotalMutationsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-mut">
<HelpTipsColumnMut />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.nonACGTN} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('non-ACGTN')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalNonAcgtnAsc} sortDesc={sortByTotalNonAcgtnDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-nonacgtn">
<HelpTipsColumnNonAcgtn />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.ns} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Ns')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalNsAsc} sortDesc={sortByTotalNsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-missing">
<HelpTipsColumnMissing />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.gaps} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Gaps')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalGapsAsc} sortDesc={sortByTotalGapsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-gaps">
<HelpTipsColumnGaps />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.insertions} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('Ins.')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalInsertionsAsc} sortDesc={sortByTotalInsertionsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-insertions">
<HelpTipsColumnInsertions />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.frameShifts} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('FS')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalFrameShiftsAsc} sortDesc={sortByTotalFrameShiftsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-frame-shifts">
<HelpTipsColumnFrameShifts />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.stopCodons} grow={0} shrink={0}>
<TableHeaderCellContent>
<TableCellText>{t('SC')}</TableCellText>
<ResultsControlsSort sortAsc={sortByTotalStopCodonsAsc} sortDesc={sortByTotalStopCodonsDesc} />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-stop-codons">
<HelpTipsColumnStopCodons />
</ButtonHelpStyled>
</TableHeaderCell>
<TableHeaderCell basis={columnWidthsPx.sequenceView} grow={1} shrink={0}>
<TableHeaderCellContent>
<SequenceSelector />
</TableHeaderCellContent>
<ButtonHelpStyled identifier="btn-help-col-seq-view" tooltipWidth="600px">
<HelpTipsColumnSeqView />
</ButtonHelpStyled>
</TableHeaderCell>
</TableHeaderRow>
<AutoSizer>
{({ width, height }) => {
return (
<FixedSizeList
overscanCount={10}
style={LIST_STYLE}
width={width}
height={height - HEADER_ROW_HEIGHT}
itemCount={rowData.length}
itemSize={ROW_HEIGHT}
itemData={rowData}
>
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
{/* @ts-ignore */}
{ResultsTableRow}
</FixedSizeList>
)
}}
</AutoSizer>
</Table>
)
}