lodash#identity TypeScript Examples
The following examples show how to use
lodash#identity.
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: utilities.ts From react-native-jigsaw with MIT License | 7 votes |
export function extractBorderAndMarginStyles(
style: StyleProp<any>,
additionalBorderStyles?: string[],
additionalMarginStyles?: string[]
) {
const flatStyle = StyleSheet.flatten(style || {});
const borderStyles = pickBy(
pick(flatStyle, [
...borderStyleNames,
...(additionalBorderStyles ? additionalBorderStyles : []),
]),
identity
);
const marginStyles = pickBy(
pick(flatStyle, [
...marginStyleNames,
...(additionalMarginStyles ? additionalMarginStyles : []),
]),
identity
);
return { borderStyles, marginStyles };
}
Example #2
Source File: flow.spec.ts From s-libs with MIT License | 6 votes |
describe('flow()', () => {
//
// stolen from https://github.com/lodash/lodash
//
it('should supply each function with the return value of the previous', () => {
const increment = (x: number): number => x + 1;
const square = (x: number): number => x * x;
const fixed = (n: number): string => n.toFixed(1);
expect(flow(increment, square, fixed)(2)).toBe('9.0');
});
it('should return an identity function when no arguments are given', () => {
expect(flow()('a')).toBe('a');
});
it('should work with a curried function and `_.head`', () => {
const curried: any = curry(identity);
const combined: any = flow(head as any, curried);
expect(combined([1])).toBe(1);
});
});
Example #3
Source File: flow-right.spec.ts From s-libs with MIT License | 6 votes |
describe('flowRight()', () => {
//
// stolen from https://github.com/lodash/lodash
//
it('should supply each function with the return value of the previous', () => {
const increment = (x: number): number => x + 1;
const square = (x: number): number => x * x;
const fixed = (n: number): string => n.toFixed(1);
expect(flowRight(fixed, square, increment)(2)).toBe('9.0');
});
it('should return an identity function when no arguments are given', () => {
expect(flowRight()('a')).toBe('a');
});
it('should work with a curried function and `_.head`', () => {
const curried: any = curry(identity);
const combined: any = flowRight(head as any, curried);
expect(combined([1])).toBe(1);
});
});
Example #4
Source File: traceSummary.ts From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
// What's the total duration of the spans in this trace?
// 最后结束的(时间戳+耗时最大)- 第一个调用的时间戳
export function traceDuration(spans: any) {
function makeList({ timestamp, duration }: any) {
if (!timestamp) {
return [];
} else if (!duration) {
return [timestamp];
}
return [timestamp, timestamp + duration];
}
// turns (timestamp, timestamp + duration) into an ordered list
const timestamps = fp.flow(fp.flatMap(makeList), fp.sortBy(identity))(spans);
if (timestamps.length < 2) {
return null;
}
const firstTime = head(timestamps);
const lastTime = last(timestamps);
return lastTime - firstTime;
}
Example #5
Source File: MjmlToJson.ts From easy-email with MIT License | 6 votes |
export function getMetaDataFromMjml(data?: IChildrenItem): {
[key: string]: any;
} {
const mjmlHtmlAttributes = data?.children
?.filter((item) => item.tagName === 'mj-html-attributes')
.map((item) => item.children)
.flat()
.filter((item) => item && item.attributes.class === 'easy-email')
.reduce((obj: { [key: string]: any }, item) => {
if (!item) return obj;
const name = item.attributes['attribute-name'];
const isMultipleAttributes = Boolean(
item.attributes['multiple-attributes']
);
obj[name] = isMultipleAttributes
? pickBy(
{
...item.attributes,
'attribute-name': undefined,
'multiple-attributes': undefined,
class: undefined,
},
identity
)
: item.attributes[name];
return obj;
}, {});
return pickBy(mjmlHtmlAttributes, identity);
}
Example #6
Source File: sumObjectValues.ts From hub with Apache License 2.0 | 5 votes |
sumObjectValues = (data: { [key: string]: number | undefined }): number => {
const cleanData = pickBy(data, identity);
if (isEmpty(cleanData)) return 0;
return Object.values(cleanData).reduce((a, b) => a! + b!) || 0;
}
Example #7
Source File: bitriseApi.client.ts From backstage with Apache License 2.0 | 5 votes |
async getBuilds(
appSlug: string,
params?: BitriseQueryParams,
): Promise<BitriseBuildListResponse> {
const baseUrl = await this.discoveryApi.getBaseUrl('proxy');
let url = `${baseUrl}/bitrise/apps/${appSlug}/builds`;
if (params) {
url = `${url}?${qs.stringify(pickBy(params, identity))}`;
}
const response = await fetch(url);
const data = await response.json();
const builds: BitriseBuildResponseItem[] = data.data;
return {
data: builds.map(
(build: BitriseBuildResponseItem): BitriseBuildResult => {
const duration = String(
Math.round(
Interval.fromDateTimes(
DateTime.fromISO(build.started_on_worker_at),
DateTime.fromISO(build.finished_at),
).length('minutes'),
),
);
return {
id: build.build_number,
source: build.commit_view_url,
status: build.status,
statusText: build.status_text,
buildSlug: build.slug,
message: `${build.branch}`,
workflow: build.triggered_workflow,
commitHash: `${build.commit_hash}`,
triggerTime: build.triggered_at,
duration: `${duration} minutes`,
appSlug,
};
},
),
paging: data.paging,
};
}
Example #8
Source File: AnnotateLocationEntityProcessor.ts From backstage with Apache License 2.0 | 5 votes |
async preProcessEntity(
entity: Entity,
location: LocationSpec,
_: CatalogProcessorEmit,
originLocation: LocationSpec,
): Promise<Entity> {
const { integrations } = this.options;
let viewUrl;
let editUrl;
let sourceLocation;
if (location.type === 'url') {
const scmIntegration = integrations.byUrl(location.target);
viewUrl = location.target;
editUrl = scmIntegration?.resolveEditUrl(location.target);
const sourceUrl = scmIntegration?.resolveUrl({
url: './',
base: location.target,
});
if (sourceUrl) {
sourceLocation = stringifyLocationRef({
type: 'url',
target: sourceUrl,
});
}
}
return merge(
{
metadata: {
annotations: pickBy(
{
[ANNOTATION_LOCATION]: stringifyLocationRef(location),
[ANNOTATION_ORIGIN_LOCATION]:
stringifyLocationRef(originLocation),
[ANNOTATION_VIEW_URL]: viewUrl,
[ANNOTATION_EDIT_URL]: editUrl,
[ANNOTATION_SOURCE_LOCATION]: sourceLocation,
},
identity,
),
},
},
entity,
);
}
Example #9
Source File: AnnotateScmSlugEntityProcessor.ts From backstage with Apache License 2.0 | 5 votes |
async preProcessEntity(
entity: Entity,
location: LocationSpec,
): Promise<Entity> {
if (entity.kind !== 'Component' || location.type !== 'url') {
return entity;
}
const scmIntegration = this.opts.scmIntegrationRegistry.byUrl(
location.target,
);
if (!scmIntegration) {
return entity;
}
let annotation;
switch (scmIntegration.type) {
case 'github':
annotation = GITHUB_ACTIONS_ANNOTATION;
break;
case 'gitlab':
annotation = GITLAB_ACTIONS_ANNOTATION;
break;
default:
return entity;
}
let projectSlug = entity.metadata.annotations?.[annotation];
if (!projectSlug) {
const gitUrl = parseGitUrl(location.target);
projectSlug = `${gitUrl.owner}/${gitUrl.name}`;
}
return merge(
{
metadata: {
annotations: pickBy(
{
[annotation]: projectSlug,
},
identity,
),
},
},
entity,
);
}
Example #10
Source File: MetricValue.tsx From abacus with GNU General Public License v2.0 | 5 votes |
metricValueFormatData: Record<string, MetricValueFormat> = {
count: {
unit: '',
prefix: '',
postfix: '',
transform: identity,
formatter: (n: number): string => n.toLocaleString(undefined),
},
conversion: {
unit: '%',
prefix: '',
postfix: '%',
transform: (x: number): number => x * 100,
formatter: standardNumberFormatter,
},
conversion_difference: {
unit: 'pp',
prefix: '',
postfix: (
<DashedTooltip title='Percentage points.'>
<span>pp</span>
</DashedTooltip>
),
transform: (x: number): number => x * 100,
formatter: standardNumberFormatter,
},
revenue: {
unit: 'USD',
prefix: '',
postfix: <> USD</>,
transform: identity,
formatter: usdFormatter,
},
revenue_difference: {
unit: 'USD',
prefix: '',
postfix: <> USD</>,
transform: identity,
formatter: usdFormatter,
},
}
Example #11
Source File: base-data-set.ts From S2 with MIT License | 5 votes |
/**
* 获得字段格式方法
* @param field
*/
public getFieldFormatter(field: string): Formatter {
return get(this.getFieldMeta(field, this.meta), 'formatter', identity);
}
Example #12
Source File: getGeneralGlobals.ts From next-core with GNU General Public License v3.0 | 5 votes |
function getIndividualGlobal(
variableName: string,
{
collectCoverage,
widgetId,
app,
storyboardFunctions,
isStoryboardFunction,
}: GeneralGlobalsOptions
): unknown {
switch (variableName) {
case "BASE_URL":
return collectCoverage ? "/next" : getBasePath().replace(/\/$/, "");
case "FN":
return storyboardFunctions;
case "IMG":
return collectCoverage
? fakeImageFactory()
: widgetId
? widgetImagesFactory(widgetId)
: imagesFactory(app.id, app.isBuildPush);
case "I18N":
return collectCoverage
? identity
: widgetId
? widgetI18nFactory(widgetId)
: getFixedT(null, getI18nNamespace("app", app.id));
case "I18N_TEXT":
return collectCoverage ? fakeI18nText : i18nText;
case "PERMISSIONS":
return {
check: collectCoverage ? fakeCheckPermissions : checkPermissions,
};
case "THEME":
return {
getTheme: collectCoverage ? () => "light" : getTheme,
};
case "console":
return isStoryboardFunction ? getReadOnlyProxy(console) : undefined;
case "location":
return collectCoverage
? {
href: "http://localhost:3000/functions/test",
origin: "http://localhost:3000",
}
: { href: location.href, origin: location.origin };
}
}
Example #13
Source File: min-by.spec.ts From s-libs with MIT License | 5 votes |
describe('minBy()', () => {
it('can sort by any primitive', () => {
expect(minBy([0, -1, 1], identity)).toBe(-1);
expect(minBy([true, false, true], identity)).toBe(false);
expect(minBy(['b', 'a', 'c'], identity)).toBe('a');
});
//
// stolen from https://github.com/lodash/lodash
//
it('should provide correct iteratee arguments', () => {
const spy = jasmine.createSpy();
minBy([1, 2, 3], spy);
expect(spy.calls.first().args).toEqual([1]);
});
it('should treat sparse arrays as dense', () => {
const array = [1];
array[2] = 3;
const spy = jasmine.createSpy().and.returnValue(true);
minBy(array, spy);
expectCallsAndReset(spy, [1], [undefined], [3]);
});
it('should not iterate custom properties of arrays', () => {
const array = [1];
(array as any).a = 1;
const spy = jasmine.createSpy();
minBy(array, spy);
expectCallsAndReset(spy, [1]);
});
it('should ignore changes to `length`', () => {
const array = [1];
const spy = jasmine.createSpy().and.callFake(() => {
array.push(2);
return true;
});
minBy(array, spy);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should work with extremely large arrays', () => {
expect(minBy(range(0, 5e5), identity)).toBe(0);
});
it('should work with an `iteratee`', () => {
expect(minBy([1, 2, 3], (n) => -n)).toBe(3);
});
it('should work when `iteratee` returns +/-Infinity', () => {
const value = Infinity;
const object = { a: value };
const actual = minBy([object, { a: value }], (obj: { a: number }) => obj.a);
expect(actual).toBe(object);
});
});
Example #14
Source File: traceSummary.ts From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
export function getServiceNames(span: any) {
return fp.flow(
fp.map((ep: any) => ep.serviceName),
fp.filter((name) => name != null && name !== ''),
fp.uniqBy(identity),
)(endpointsForSpan(span));
}
Example #15
Source File: removeUndefined.ts From nextclade with MIT License | 5 votes |
/**
* Removes object keys which have `undefined` value
*/
export function removeUndefined<T>(obj: Record<string, T | undefined>) {
return pickBy(obj, identity) as Record<string, T>
}
Example #16
Source File: max-by.spec.ts From s-libs with MIT License | 5 votes |
describe('maxBy()', () => {
it('can sort by any primitive', () => {
expect(maxBy([0, 1, -1], identity)).toBe(1);
expect(maxBy([false, true, false], identity)).toBe(true);
expect(maxBy(['b', 'c', 'a'], identity)).toBe('c');
});
//
// stolen from https://github.com/lodash/lodash
//
it('should provide correct iteratee arguments', () => {
const spy = jasmine.createSpy();
maxBy([1, 2, 3], spy);
expect(spy.calls.first().args).toEqual([1]);
});
it('should treat sparse arrays as dense', () => {
const array = [1];
array[2] = 3;
const spy = jasmine.createSpy().and.returnValue(true);
maxBy(array, spy);
expectCallsAndReset(spy, [1], [undefined], [3]);
});
it('should not iterate custom properties of arrays', () => {
const array = [1];
(array as any).a = 1;
const spy = jasmine.createSpy();
maxBy(array, spy);
expectCallsAndReset(spy, [1]);
});
it('should ignore changes to `length`', () => {
const array = [1];
const spy = jasmine.createSpy().and.callFake(() => {
array.push(2);
return true;
});
maxBy(array, spy);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should work with extremely large arrays', () => {
expect(maxBy(range(0, 5e5), identity)).toBe(499999);
});
it('should work with an `iteratee`', () => {
expect(maxBy([1, 2, 3], (n) => -n)).toBe(1);
});
it('should work when `iteratee` returns +/-Infinity', () => {
const value = -Infinity;
const object = { a: value };
const actual = maxBy([object, { a: value }], (obj: { a: number }) => obj.a);
expect(actual).toBe(object);
});
});
Example #17
Source File: loaders-explorer.service.ts From nestjs-mercurius with MIT License | 4 votes |
private createContextCallback<T extends Record<string, any>>(
instance: T,
prototype: any,
wrapper: InstanceWrapper,
moduleRef: Module,
resolver: any,
isRequestScoped: boolean,
transform: Function = identity,
) {
const paramsFactory = this.gqlParamsFactory;
const fieldResolverEnhancers = this.gqlOptions.fieldResolverEnhancers || [];
const contextOptions = {
guards: fieldResolverEnhancers.includes('guards'),
filters: fieldResolverEnhancers.includes('filters'),
interceptors: fieldResolverEnhancers.includes('interceptors'),
};
if (isRequestScoped) {
const loaderCallback = async (...args: any[]) => {
const gqlContext = paramsFactory.exchangeKeyForValue(
GqlParamtype.CONTEXT,
undefined,
args,
);
let contextId: ContextId;
if (gqlContext && gqlContext[REQUEST_CONTEXT_ID]) {
contextId = gqlContext[REQUEST_CONTEXT_ID];
} else if (
gqlContext &&
gqlContext.req &&
gqlContext.req[REQUEST_CONTEXT_ID]
) {
contextId = gqlContext.req[REQUEST_CONTEXT_ID];
} else {
contextId = createContextId();
Object.defineProperty(gqlContext, REQUEST_CONTEXT_ID, {
value: contextId,
enumerable: false,
configurable: false,
writable: false,
});
}
this.registerContextProvider(gqlContext, contextId);
const contextInstance = await this.injector.loadPerContext(
instance,
moduleRef,
moduleRef.providers,
contextId,
);
const callback = this.externalContextCreator.create(
contextInstance,
transform(contextInstance[resolver.methodName]),
resolver.methodName,
PARAM_ARGS_METADATA,
paramsFactory,
contextId,
wrapper.id,
contextOptions,
'graphql',
);
return callback(...args);
};
return this.registerFieldMiddlewareIfExists(
loaderCallback,
instance,
resolver.methodName,
);
}
const loaderCallback = this.externalContextCreator.create(
instance,
prototype[resolver.methodName],
resolver.methodName,
PARAM_ARGS_METADATA,
paramsFactory,
undefined,
undefined,
contextOptions,
'graphql',
);
return this.registerFieldMiddlewareIfExists(
loaderCallback,
instance,
resolver.methodName,
);
}
Example #18
Source File: MetricAssignmentResults.tsx From abacus with GNU General Public License v2.0 | 4 votes |
/**
* Display results for a MetricAssignment
*/
export default function MetricAssignmentResults({
strategy,
metricAssignment,
metric,
analysesByStrategyDateAsc,
experiment,
recommendation,
variationDiffKey,
}: {
strategy: AnalysisStrategy
metricAssignment: MetricAssignment
metric: Metric
analysesByStrategyDateAsc: Record<AnalysisStrategy, Analysis[]>
experiment: ExperimentFull
recommendation: Recommendations.Recommendation
variationDiffKey: string
}): JSX.Element | null {
const classes = useStyles()
const [isShowObservedData, setIsShowObservedData] = useState<boolean>(false)
const toggleIsShowObservedData = () => {
setIsShowObservedData((isShowObservedData) => !isShowObservedData)
}
const isConversion = metric.parameterType === MetricParameterType.Conversion
const estimateTransform: (estimate: number | null) => number | null = isConversion
? (estimate: number | null) => estimate && estimate * 100
: identity
const analyses = analysesByStrategyDateAsc[strategy]
const latestAnalysis = _.last(analyses)
const latestEstimates = latestAnalysis?.metricEstimates
if (!latestAnalysis || !latestEstimates) {
return <MissingAnalysisMessage />
}
const [_changeVariationId, baseVariationId] = variationDiffKey.split('_')
const dates = analyses.map(({ analysisDatetime }) => analysisDatetime.toISOString())
const plotlyDataVariationGraph: Array<Partial<PlotData>> = [
..._.flatMap(experiment.variations, (variation, index) => {
return [
{
name: `${variation.name}: lower bound`,
x: dates,
y: analyses
.map(
({ metricEstimates }) => metricEstimates && metricEstimates.variations[variation.variationId].bottom_95,
)
.map(estimateTransform),
line: {
color: Visualizations.variantColors[index],
},
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `${variation.name}: upper bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.variations[variation.variationId].top_95)
.map(estimateTransform),
line: {
color: Visualizations.variantColors[index],
},
fill: 'tonexty' as const,
fillcolor: Visualizations.variantColors[index],
mode: 'lines' as const,
type: 'scatter' as const,
},
]
}),
]
const plotlyDataDifferenceGraph: Array<Partial<PlotData>> = [
{
name: `difference: 99% lower bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_99)
.map(estimateTransform),
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `difference: 99% upper bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_99)
.map(estimateTransform),
fill: 'tonexty',
fillcolor: 'rgba(0,0,0,.2)',
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `difference: 95% lower bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_95)
.map(estimateTransform),
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `difference: 95% upper bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_95)
.map(estimateTransform),
fill: 'tonexty',
fillcolor: 'rgba(0,0,0,.2)',
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `difference: 50% lower bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_50)
.map(estimateTransform),
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: `difference: 50% upper bound`,
x: dates,
y: analyses
.map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_50)
.map(estimateTransform),
fill: 'tonexty',
fillcolor: 'rgba(0,0,0,.2)',
line: { width: 0 },
marker: { color: '444' },
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: 'ROPE: lower bound',
x: dates,
y: analyses.map((_) => -metricAssignment.minDifference).map(estimateTransform),
line: {
color: 'rgba(0,0,0,.4)',
dash: 'dash',
},
mode: 'lines' as const,
type: 'scatter' as const,
},
{
name: 'ROPE: upper bound',
x: dates,
y: analyses.map((_) => metricAssignment.minDifference).map(estimateTransform),
line: {
color: 'rgba(0,0,0,.4)',
dash: 'dash',
},
mode: 'lines' as const,
type: 'scatter' as const,
},
]
return (
<div className={clsx(classes.root, 'analysis-detail-panel')}>
<Typography className={classes.dataTableHeader}>Summary</Typography>
<TableContainer component={Paper}>
<Table>
<TableBody>
<TableRow>
<TableCell>
<Typography variant='h5' gutterBottom className={classes.recommendation}>
<AnalysisDisplay {...{ experiment, analysis: recommendation }} />
</Typography>
{recommendation.decision === Recommendations.Decision.ManualAnalysisRequired && (
<Typography variant='body1' gutterBottom>
<strong> Different strategies are recommending conflicting variations! </strong>
</Typography>
)}
<Typography variant='body1'>
{getOverviewMessage(experiment, recommendation)}{' '}
<Link
href={`https://github.com/Automattic/experimentation-platform/wiki/Experimenter's-Guide#reading-the-data`}
target='_blank'
>
Learn more
</Link>
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography variant='body1' gutterBottom>
The absolute change in the {isConversion ? 'conversion rate' : 'ARPU'} of{' '}
<MetricValue
metricParameterType={metric.parameterType}
isDifference={true}
value={latestEstimates.diffs[variationDiffKey].bottom_95}
displayPositiveSign
displayUnit={false}
/>{' '}
to{' '}
<MetricValue
metricParameterType={metric.parameterType}
isDifference={true}
value={latestEstimates.diffs[variationDiffKey].top_95}
displayPositiveSign
/>{' '}
is {recommendation.statisticallySignificant ? '' : ' not '}
statistically different from zero because the interval
{recommendation.statisticallySignificant ? ' excludes ' : ' includes '}
zero.{' '}
{
explanationLine2[
recommendation.practicallySignificant as Recommendations.PracticalSignificanceStatus
]
}
<MetricValue
metricParameterType={metric.parameterType}
isDifference={true}
value={-metricAssignment.minDifference}
displayPositiveSign
displayUnit={false}
/>{' '}
to{' '}
<MetricValue
metricParameterType={metric.parameterType}
isDifference={true}
value={metricAssignment.minDifference}
displayPositiveSign
/>
.
</Typography>
<strong>Last analyzed:</strong>{' '}
<DatetimeText datetime={latestAnalysis.analysisDatetime} excludeTime={true} />.
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<strong>Metric description:</strong> {metric.description}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<Typography className={classes.dataTableHeader}>Analysis</Typography>
<TableContainer component={Paper}>
<Table className={classes.coolTable}>
<TableHead>
<TableRow>
<TableCell>Variant</TableCell>
<TableCell align='right'>
{metric.parameterType === MetricParameterType.Revenue
? 'Average revenue per user (ARPU) interval'
: 'Conversion rate interval'}
</TableCell>
<TableCell align='right'>Absolute change</TableCell>
<TableCell align='right'>Relative change (lift)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{experiment.variations.map((variation) => (
<React.Fragment key={variation.variationId}>
<TableRow>
<TableCell
component='th'
scope='row'
variant='head'
valign='top'
className={clsx(classes.rowHeader, classes.headerCell, classes.credibleIntervalHeader)}
>
<span className={classes.monospace}>{variation.name}</span>
</TableCell>
<TableCell className={classes.monospace} align='right'>
<MetricValueInterval
intervalName={'the metric value'}
metricParameterType={metric.parameterType}
bottomValue={latestEstimates.variations[variation.variationId].bottom_95}
topValue={latestEstimates.variations[variation.variationId].top_95}
displayPositiveSign={false}
/>
</TableCell>
<TableCell className={classes.monospace} align='right'>
{variation.isDefault ? (
'Baseline'
) : (
<MetricValueInterval
intervalName={'the absolute change between variations'}
metricParameterType={metric.parameterType}
isDifference={true}
bottomValue={latestEstimates.diffs[`${variation.variationId}_${baseVariationId}`].bottom_95}
topValue={latestEstimates.diffs[`${variation.variationId}_${baseVariationId}`].top_95}
/>
)}
</TableCell>
<TableCell className={classes.monospace} align='right'>
{variation.isDefault ? (
'Baseline'
) : (
<MetricValueInterval
intervalName={'the relative change between variations'}
metricParameterType={MetricParameterType.Conversion}
bottomValue={Analyses.ratioToDifferenceRatio(
latestEstimates.ratios[`${variation.variationId}_${baseVariationId}`].bottom_95,
)}
topValue={Analyses.ratioToDifferenceRatio(
latestEstimates.ratios[`${variation.variationId}_${baseVariationId}`].top_95,
)}
/>
)}
</TableCell>
</TableRow>
</React.Fragment>
))}
</TableBody>
</Table>
</TableContainer>
<Typography className={classes.analysisFinePrint}>
95% Credible Intervals (CIs). <strong> Experimenter-set minimum practical difference: </strong>{' '}
<MetricValue
value={metricAssignment.minDifference}
metricParameterType={metric.parameterType}
isDifference={true}
/>
.
</Typography>
{dates.length > 1 ? (
<div className={classes.metricEstimatePlots}>
<Plot
layout={{
...Visualizations.plotlyLayoutDefault,
title: isConversion
? `Conversion rate estimates by variation (%)`
: `Revenue estimates by variation (USD)`,
}}
data={plotlyDataVariationGraph}
className={classes.metricEstimatePlot}
/>
<Plot
layout={{
...Visualizations.plotlyLayoutDefault,
title: isConversion
? `Conversion rate difference estimates (percentage points)`
: `Revenue difference estimates (USD)`,
}}
data={plotlyDataDifferenceGraph}
className={classes.metricEstimatePlot}
/>
</div>
) : (
<Typography variant='body1' className={classes.noPlotMessage}>
Past values will be plotted once we have more than one day of results.
</Typography>
)}
<Typography
className={clsx(classes.dataTableHeader, classes.clickable)}
onClick={toggleIsShowObservedData}
role='button'
>
{isShowObservedData ? (
<ExpandMore className={classes.expandCollapseIcon} />
) : (
<ChevronRight className={classes.expandCollapseIcon} />
)}
"Observed" data
</Typography>
{isShowObservedData && (
<>
<TableContainer component={Paper}>
<Table className={classes.coolTable}>
<TableHead>
<TableRow>
<TableCell>Variant</TableCell>
<TableCell align='right'>Users</TableCell>
<TableCell align='right'>
{metric.parameterType === MetricParameterType.Revenue ? 'Revenue' : 'Conversions'}
</TableCell>
<TableCell align='right'>
{metric.parameterType === MetricParameterType.Revenue
? 'Average revenue per user (ARPU)'
: 'Conversion rate'}
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{experiment.variations.map((variation) => (
<React.Fragment key={variation.variationId}>
<TableRow>
<TableCell
component='th'
scope='row'
variant='head'
valign='top'
className={clsx(classes.rowHeader, classes.headerCell, classes.credibleIntervalHeader)}
>
<span className={classes.monospace}>{variation.name}</span>
</TableCell>
<TableCell className={classes.monospace} align='right'>
{latestAnalysis.participantStats[`variation_${variation.variationId}`].toLocaleString()}
</TableCell>
<TableCell className={classes.monospace} align='right'>
<MetricValue
value={
latestAnalysis.participantStats[`variation_${variation.variationId}`] *
latestEstimates.variations[variation.variationId].mean
}
metricParameterType={
metric.parameterType === MetricParameterType.Conversion
? MetricParameterType.Count
: metric.parameterType
}
/>
</TableCell>
<TableCell className={classes.monospace} align='right'>
<MetricValue
value={latestEstimates.variations[variation.variationId].mean}
metricParameterType={metric.parameterType}
/>
</TableCell>
</TableRow>
</React.Fragment>
))}
</TableBody>
</Table>
</TableContainer>
<Typography variant='caption' gutterBottom>
<Link href='https://wp.me/PCYsg-Fqg/#observed-data-uses-posterior-means' target='_blank'>
"Observed" data as produced from our model, not raw observed data.
</Link>{' '}
For illustrative purposes only.
</Typography>
</>
)}
</div>
)
}
Example #19
Source File: partial.spec.ts From s-libs with MIT License | 4 votes |
describe('partial()', () => {
it('does not alter the `this` binding', () => {
function fn(this: any): any {
return this;
}
expect(partial(fn.bind(2))()).toBe(2);
expect(partial(fn)()).toBe(undefined);
});
it('sets the `length` property', () => {
const fn = (_a: any, _b: any, _c: any): void => {
// blah
};
expect(partial(fn).length).toBe(3);
expect(partial(fn, 1).length).toBe(2);
expect(partial(fn, 1, 2).length).toBe(1);
expect(partial(fn, 1, 2, 3).length).toBe(0);
});
//
// stolen from https://github.com/lodash/lodash
//
it('partially applies arguments', () => {
expect(partial(identity as (value: string) => string, 'a')()).toBe('a');
});
it('creates a function that can be invoked with additional arguments', () => {
const fn = (a: string, b: string): string[] => [a, b];
const par = partial(fn, 'a');
expect(par('b')).toEqual(['a', 'b']);
});
it('works when there are no partially applied arguments and the created function is invoked without additional arguments', () => {
const fn = function (): number {
return arguments.length;
};
const par = partial(fn);
expect(par()).toBe(0);
});
it('works when there are no partially applied arguments and the created function is invoked with additional arguments', () => {
expect(partial(identity as (value: string) => string)('a')).toBe('a');
});
it('should ensure `new par` is an instance of `func`', () => {
const object = {};
function Foo(value = false): false | {} {
return value && object;
}
const par = partial(Foo);
expect(new (par as any)() instanceof Foo).toBeTruthy();
expect(new (par as any)(true)).toBe(object);
});
it('should clone metadata for created functions', () => {
function greet(greeting: string, name: string): string {
return greeting + ' ' + name;
}
const par1 = partial(greet, 'hi');
const par2 = partial(par1, 'barney');
const par3 = partial(par1, 'pebbles');
expect(par1('fred')).toBe('hi fred');
expect(par2()).toBe('hi barney');
expect(par3()).toBe('hi pebbles');
});
it('should work with curried functions', () => {
const fn = (a: number, b: number, c: number): number => a + b + c;
const curried = curry(partial(fn, 1), 2);
expect(curried(2, 3)).toBe(6);
expect(curried(2)(3)).toBe(6);
});
});
Example #20
Source File: memoize.spec.ts From s-libs with MIT License | 4 votes |
describe('memoize()', () => {
//
// stolen from https://github.com/lodash/lodash
//
it('should memoize results based on the first argument given', () => {
const memoized = memoize((a: number, b: number, c: number) => a + b + c);
expect(memoized(1, 2, 3)).toBe(6);
expect(memoized(1, 3, 5)).toBe(6);
});
it('should support a `resolver`', () => {
const fn = (a: number, b: number, c: number): number => a + b + c;
const memoized = memoize(fn, fn);
expect(memoized(1, 2, 3)).toBe(6);
expect(memoized(1, 3, 5)).toBe(9);
});
it('should use `this` binding of function for `resolver`', () => {
function fn(this: any, a: number): any {
return a + this.b + this.c;
}
const memoized = memoize(fn, fn);
const object = { memoized, b: 2, c: 3 };
expect(object.memoized(1)).toBe(6);
object.b = 3;
object.c = 5;
expect(object.memoized(1)).toBe(9);
});
it('should not error if `resolver` is nullish', () => {
expect(isFunction(memoize(noop))).toBeTruthy();
expect(isFunction(memoize(noop, null as any))).toBeTruthy();
expect(isFunction(memoize(noop, undefined))).toBeTruthy();
});
it('should check cache for own properties', () => {
const props = [
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf',
];
const memoized = memoize(identity);
for (const value of props) {
expect(memoized(value)).toBe(value);
}
});
it('should cache the `__proto__` key', () => {
const array: any[] = [];
const key = '__proto__';
let count = 0;
function func(_arg: any): any[] {
++count;
return array;
}
let memoized = memoize(func);
memoized(key);
memoized(key);
expect(count).toBe(1);
expect(memoized.cache.get(key)).toBe(array);
expect(memoized.cache.delete(key)).toBe(true);
memoized = memoize(func, identity);
memoized(key);
memoized(key);
expect(count).toBe(2);
expect(memoized.cache.get(key)).toBe(array);
expect(memoized.cache.delete(key)).toBe(true);
});
});
Example #21
Source File: every.spec.ts From s-libs with MIT License | 4 votes |
describe('every()', () => {
//
// stolen from https://github.com/lodash/lodash
//
it('returns `true` if `predicate` returns truthy for all elements', () => {
expect(every([true, 1, 'a'], identity)).toBe(true);
});
it('should return `true` for empty collections', () => {
for (const empty of [[], {}, null, undefined, false, 0, NaN, '']) {
expect(every(empty, identity)).toBe(true);
}
});
it('should return `false` as soon as `predicate` returns falsey', () => {
let count = 0;
const result = every([true, null, true], (value) => {
++count;
return value;
});
expect(result).toBe(false);
expect(count).toBe(2);
});
it('should work with collections of `undefined` values', () => {
expect(every([undefined, undefined, undefined], identity)).toBe(false);
});
it('should provide correct iteratee arguments', () => {
const spy = jasmine.createSpy();
every([1, 2, 3], spy);
expect(spy.calls.first().args).toEqual([1, 0]);
});
it('should treat sparse arrays as dense', () => {
const array = [1];
array[2] = 3;
const spy = jasmine.createSpy().and.returnValue(true);
every(array, spy);
expectCallsAndReset(spy, [1, 0], [undefined, 1], [3, 2]);
});
it('should not iterate custom properties of arrays', () => {
const array = [1];
(array as any).a = 1;
const spy = jasmine.createSpy().and.returnValue(true);
every(array, spy);
expectCallsAndReset(spy, [1, 0]);
});
it('iterates over own string keyed properties of objects', () => {
const object = { a: 1 };
const spy = jasmine.createSpy().and.returnValue(true);
every(object, spy);
expectCallsAndReset(spy, [1, 'a']);
});
it('should ignore changes to `length`', () => {
const array = [1];
const spy = jasmine.createSpy().and.callFake(() => {
array.push(2);
return true;
});
every(array, spy);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should ignore added `object` properties', () => {
const object: any = { a: 1 };
const spy = jasmine.createSpy().and.callFake(() => {
object.b = 2;
return true;
});
every(object, spy);
expect(spy).toHaveBeenCalledTimes(1);
});
});
Example #22
Source File: JsonToMjml.ts From easy-email with MIT License | 4 votes |
export function JsonToMjml(options: JsonToMjmlOption): string {
const {
data,
idx = 'content',
context = data,
mode = 'production',
dataSource = {},
} = options;
if (
(isBoolean(data?.data?.hidden) && data?.data?.hidden) ||
data?.data?.hidden === 'true'
) {
return '';
}
const att = pickBy(
{
...data.attributes,
},
identity
);
const isTest = mode === 'testing';
const keepClassName = isProductionMode(options) ? options.keepClassName : false;
const placeholder = isTest ? renderPlaceholder(data.type) : '';
if (isTest && idx) {
att['css-class'] = classnames(
att['css-class'],
'email-block',
getNodeIdxClassName(idx),
getNodeTypeClassName(data.type)
);
}
if (keepClassName) {
att['css-class'] = classnames(att['css-class'], getNodeTypeClassName(data.type));
}
if (isTest && data.type === BasicType.TEXT) {
att['css-class'] = classnames(att['css-class'], MERGE_TAG_CLASS_NAME);
}
if (data.type === BasicType.PAGE) {
att['css-class'] = classnames(att['css-class'], 'mjml-body');
}
const attributeStr = Object.keys(att)
.filter((key) => att[key] !== '') // filter att=""
.map((key) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const val = isString(att[key]) ? att[key].replace(/"/gm, '') : att[key];
return `${key}="${val}"`;
})
.join(' ');
const block = BlockManager.getBlockByType(data.type);
if (!block) {
throw new Error(
`Can not find ${data.type} block!!! Have you registered this block ?`
);
}
if (block.render) {
const transformBlockData = block.render(
data,
idx,
mode,
context,
dataSource
);
if (!transformBlockData) return '';
const transformData = isValidElement(transformBlockData)
? parseReactBlockToBlockData(transformBlockData)
: transformBlockData;
att['css-class'] = [
...new Set(
classnames(
isTest && getPreviewClassName(idx, data.type),
transformData?.['attributes']?.['css-class']
).split(' ')
),
].join(' ');
return JsonToMjml({
data: {
...transformData,
attributes: {
...transformData.attributes,
'css-class': att['css-class'],
},
},
idx: null,
context: context,
dataSource,
mode,
keepClassName
});
}
const children = data.children
.map((child, index) => {
let childIdx = idx ? getChildIdx(idx, index) : null;
if (data.type === BasicType.TEMPLATE) {
childIdx = getChildIdx(data.data.value.idx, index);
if (!data.data.value.idx) {
childIdx = null;
}
}
return JsonToMjml({
data: child,
idx: childIdx,
dataSource,
context,
mode,
keepClassName
});
})
.join('\n');
switch (data.type) {
case BasicType.TEMPLATE:
return children || data.data.value.content;
case BasicType.PAGE:
const metaData = generaMjmlMetaData(data);
const value: IPage['data']['value'] = data.data.value;
const breakpoint = value.breakpoint
? `<mj-breakpoint width="${data.data.value.breakpoint}" />`
: '';
const nonResponsive = !value.responsive
? `<mj-raw>
<meta name="viewport" />
</mj-raw>
<mj-style inline="inline">.mjml-body { width: ${data.attributes.width || '600px'
}; margin: 0px auto; }</mj-style>`
: '';
const styles =
value.headStyles
?.map(
(style) =>
`<mj-style ${style.inline ? 'inline="inline"' : ''}>${style.content
}</mj-style>`
)
.join('\n') || '';
const userStyle = value['user-style']
? `<mj-style ${value['user-style'].inline ? 'inline="inline"' : ''}>${value['user-style'].content
}</mj-style>`
: '';
const extraHeadContent = value.extraHeadContent ? `<mj-raw>${value.extraHeadContent}</mj-raw>` : '';
return `
<mjml>
<mj-head>
${metaData}
${nonResponsive}
${styles}
${userStyle}
${breakpoint}
${extraHeadContent}
${value.fonts
?.filter(Boolean)
.map(
(item) =>
`<mj-font name="${item.name}" href="${item.href}" />`
)}
<mj-attributes>
${value.headAttributes}
${value['font-family']
? `<mj-all font-family="${value['font-family'].replace(/"/gm, '')}" />`
: ''
}
${value['font-size']
? `<mj-text font-size="${value['font-size']}" />`
: ''
}
${value['text-color']
? `<mj-text color="${value['text-color']}" />`
: ''
}
${value['line-height']
? `<mj-text line-height="${value['line-height']}" />`
: ''
}
${value['font-weight']
? `<mj-text font-weight="${value['font-weight']}" />`
: ''
}
${value['content-background-color']
? `<mj-wrapper background-color="${value['content-background-color']}" />
<mj-section background-color="${value['content-background-color']}" />
`
: ''
}
</mj-attributes>
</mj-head>
<mj-body ${attributeStr}>
${children}
</mj-body>
</mjml>
`;
case BasicType.COLUMN:
return `
<mj-column ${attributeStr}>
${children || placeholder}
</mj-column>
`;
case BasicType.SECTION:
return `
<mj-section ${attributeStr}>
${children || `<mj-column>${placeholder}</mj-column>`}
</mj-section>
`;
case BasicType.GROUP:
return `
<mj-group ${attributeStr}>
${children || `<mj-column>${placeholder}</mj-column>`}
</mj-group>
`;
case BasicType.WRAPPER:
return `
<mj-wrapper ${attributeStr}>
${children ||
`<mj-section><mj-column>${placeholder}</mj-column></mj-section>`
}
</mj-wrapper>
`;
case BasicType.CAROUSEL:
const carouselImages = (data as ICarousel).data.value.images
.map((image, index) => {
const imageAttributeStr = Object.keys(image)
.filter((key) => key !== 'content' && att[key] !== '') // filter att=""
.map((key) => `${key}="${image[key as keyof typeof image]}"`)
.join(' ');
return `
<mj-carousel-image ${imageAttributeStr} />
`;
})
.join('\n');
return `
<mj-carousel ${attributeStr}>
${carouselImages}
</mj-carousel>
`;
case BasicType.NAVBAR:
const links = (data as INavbar).data.value.links
.map((link, index) => {
const linkAttributeStr = Object.keys(link)
.filter((key) => key !== 'content' && att[key] !== '') // filter att=""
.map((key) => `${key}="${link[key as keyof typeof link]}"`)
.join(' ');
return `
<mj-navbar-link ${linkAttributeStr}>${link.content}</mj-navbar-link>
`;
})
.join('\n');
return `
<mj-navbar ${attributeStr}>
${links}
</mj-navbar>
`;
case BasicType.SOCIAL:
const elements = (data as ISocial).data.value.elements
.map((element, index) => {
const elementAttributeStr = Object.keys(element)
.filter((key) => key !== 'content' && att[key] !== '') // filter att=""
.map((key) => `${key}="${element[key as keyof typeof element]}"`)
.join(' ');
return `
<mj-social-element ${elementAttributeStr}>${element.content}</mj-social-element>
`;
})
.join('\n');
return `
<mj-social ${attributeStr}>
${elements}
</mj-social>
`;
case BasicType.RAW:
return `
<mj-raw ${attributeStr}>
${data.data.value?.content}
</mj-raw>
`;
case BasicType.IMAGE:
if (mode === 'testing') {
const url = data.attributes.src;
if (
url === '' ||
/{{([\s\S]+?)}}/g.test(url) ||
/\*\|([^\|\*]+)\|\*/g.test(url)
) {
return `<mj-image src="${getImg(
'IMAGE_59'
)}" ${attributeStr}></mj-image>`;
}
}
return `<mj-image ${attributeStr}></mj-image>`;
default:
return `
<mj-${data.type} ${attributeStr}>
${children || data.data.value?.content || ''}
</mj-${data.type}>
`;
}
}
Example #23
Source File: Picker.tsx From react-native-jigsaw with MIT License | 4 votes |
Picker: React.FC<PickerProps> = ({
error,
options = [],
onValueChange,
defaultValue,
Icon,
style,
placeholder,
value,
disabled = false,
theme,
assistiveText,
label,
iconColor = unstyledColor,
iconSize = 24,
leftIconMode = "inset",
leftIconName,
placeholderTextColor = unstyledColor,
rightIconName,
type = "solid",
}) => {
const androidPickerRef = React.useRef<any | undefined>(undefined);
const [internalValue, setInternalValue] = React.useState<string | undefined>(
value || defaultValue
);
const [pickerVisible, setPickerVisible] = React.useState(false);
const togglePickerVisible = () => {
setPickerVisible(!pickerVisible);
};
React.useEffect(() => {
if (value != null) {
setInternalValue(value);
}
}, [value]);
React.useEffect(() => {
if (defaultValue != null) {
setInternalValue(defaultValue);
}
}, [defaultValue]);
React.useEffect(() => {
if (pickerVisible && androidPickerRef.current) {
androidPickerRef?.current?.focus();
}
}, [pickerVisible, androidPickerRef]);
const normalizedOptions = normalizeOptions(options);
const pickerOptions = placeholder
? [{ value: placeholder, label: placeholder }, ...normalizedOptions]
: normalizedOptions;
const { colors } = theme;
const { viewStyles, textStyles } = extractStyles(style);
const additionalBorderStyles = ["backgroundColor"];
const additionalMarginStyles = [
"bottom",
"height",
"left",
"maxHeight",
"maxWidth",
"minHeight",
"minWidth",
"overflow",
"position",
"right",
"top",
"width",
"zIndex",
];
const {
borderStyles: extractedBorderStyles,
marginStyles: extractedMarginStyles,
} = extractBorderAndMarginStyles(
viewStyles,
additionalBorderStyles,
additionalMarginStyles
);
const borderStyles = {
...{
...(type === "solid"
? {
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 5,
borderTopWidth: 1,
borderRightWidth: 1,
borderLeftWidth: 1,
}
: {}),
borderBottomWidth: 1,
borderColor: unstyledColor,
borderStyle: "solid",
},
...extractedBorderStyles,
...(error ? { borderColor: errorColor } : {}),
...(disabled
? { borderColor: "transparent", backgroundColor: disabledColor }
: {}),
};
const marginStyles = {
height: 60,
...extractedMarginStyles,
};
const stylesWithoutBordersAndMargins = omit(viewStyles, [
...borderStyleNames,
...marginStyleNames,
...additionalBorderStyles,
...additionalMarginStyles,
]);
const selectedLabel =
internalValue &&
((pickerOptions as unknown as PickerOption[]).find(
(option) => option.value === internalValue
)?.label ??
internalValue);
const labelText = label ? (
<Text
style={{
textAlign: textStyles.textAlign,
color: unstyledColor,
fontSize: 12,
paddingBottom: 4,
}}
>
{label}
</Text>
) : null;
const leftIconOutset = leftIconMode === "outset";
const leftIcon = leftIconName ? (
<Icon
name={leftIconName}
color={disabled ? unstyledColor : iconColor}
size={iconSize}
style={{
marginRight: 4,
marginLeft: 4,
}}
/>
) : null;
const rightIcon = rightIconName ? (
<Icon
name={rightIconName}
color={disabled ? unstyledColor : iconColor}
size={iconSize}
style={{
marginRight: -10,
marginLeft: 8,
}}
/>
) : null;
const textAlign = textStyles?.textAlign;
const calculateLeftPadding = () => {
if (leftIconOutset) {
if (textAlign === "center") {
return iconSize - Math.abs(8 - iconSize);
}
return iconSize + 8;
}
return 0;
};
const assistiveTextLabel = assistiveText ? (
<Text
style={{
textAlign,
width: "100%",
paddingLeft: calculateLeftPadding(),
color: unstyledColor,
fontSize: 12,
paddingTop: 4,
}}
>
{assistiveText}
</Text>
) : null;
const primaryTextStyle = {
color: unstyledColor,
fontSize: 14,
...pickBy(textStyles, identity),
...(placeholder === internalValue ? { color: placeholderTextColor } : {}),
...(disabled ? { color: unstyledColor } : {}),
};
const handleValueChange = (newValue: string, itemIndex: number) => {
if (!placeholder || itemIndex > 0) {
onValueChange?.(newValue, itemIndex);
}
setInternalValue(newValue);
};
return (
/* marginsContainer */
<View style={[styles.marginsContainer, marginStyles]}>
{/* touchableContainer */}
<Touchable
disabled={disabled}
onPress={togglePickerVisible}
style={styles.touchableContainer}
>
{/* outsetContainer */}
<View
pointerEvents="none"
style={[
styles.outsetContainer,
stylesWithoutBordersAndMargins,
!leftIconOutset ? (borderStyles as PickerProps["style"]) : {},
]}
>
{leftIcon}
{/* insetContainer */}
<View
style={[
styles.insetContainer,
leftIconOutset ? (borderStyles as PickerProps["style"]) : {},
]}
>
{/* primaryTextContainer */}
<View style={styles.primaryTextContainer}>
{labelText}
<Text style={primaryTextStyle}>
{String(selectedLabel ?? placeholder)}
</Text>
</View>
{rightIcon}
</View>
</View>
{assistiveTextLabel}
</Touchable>
{/* iosPicker */}
{isIos && pickerVisible ? (
<Portal>
<View
style={[
styles.iosPicker,
{
backgroundColor: colors.divider,
},
]}
>
<SafeAreaView style={styles.iosSafeArea}>
<Button
Icon={Icon}
type="text"
onPress={togglePickerVisible}
style={styles.iosButton}
>
{"Close"}
</Button>
<NativePicker
style={styles.iosNativePicker}
selectedValue={internalValue}
onValueChange={handleValueChange}
>
{(pickerOptions as unknown as PickerOption[]).map((option) => (
<NativePicker.Item
label={option.label}
value={option.value}
key={option.value}
/>
))}
</NativePicker>
</SafeAreaView>
</View>
</Portal>
) : null}
{/* nonIosPicker */}
{!isIos && pickerVisible ? (
<NativePicker
enabled={pickerVisible}
selectedValue={internalValue}
onValueChange={handleValueChange}
style={styles.nonIosPicker}
ref={androidPickerRef}
onBlur={() => setPickerVisible(false)}
>
{(pickerOptions as unknown as PickerOption[]).map((option) => (
<NativePicker.Item
label={option.label}
value={option.value}
key={option.value}
/>
))}
</NativePicker>
) : null}
</View>
);
}
Example #24
Source File: useSpreadSheet.ts From S2 with MIT License | 4 votes |
export function useSpreadSheet(props: SheetComponentsProps) {
const forceUpdate = useUpdate();
const s2Ref = React.useRef<SpreadSheet>();
const containerRef = React.useRef<HTMLDivElement>();
const wrapperRef = React.useRef<HTMLDivElement>();
const {
spreadsheet: customSpreadSheet,
dataCfg,
options,
themeCfg,
sheetType,
onSheetUpdate = identity,
} = props;
/** 保存重渲 effect 的 deps */
const updatePrevDepsRef = React.useRef<[S2DataConfig, S2Options, ThemeCfg]>([
dataCfg,
options,
themeCfg,
]);
const { loading, setLoading } = useLoading(s2Ref.current, props.loading);
const pagination = usePagination(s2Ref.current, props);
useEvents(props, s2Ref.current);
const renderSpreadSheet = React.useCallback(
(container: HTMLDivElement) => {
const s2Options = getSheetComponentOptions(options);
const s2Constructor: S2Constructor = [container, dataCfg, s2Options];
if (customSpreadSheet) {
return customSpreadSheet(...s2Constructor);
}
if (sheetType === 'table') {
return new TableSheet(container, dataCfg, s2Options);
}
return new PivotSheet(container, dataCfg, s2Options);
},
[sheetType, options, dataCfg, customSpreadSheet],
);
const buildSpreadSheet = React.useCallback(() => {
setLoading(true);
s2Ref.current = renderSpreadSheet(containerRef.current);
s2Ref.current.setThemeCfg(props.themeCfg);
s2Ref.current.render();
setLoading(false);
// 子 hooks 内使用了 s2Ref.current 作为 dep
// forceUpdate 一下保证子 hooks 能 rerender
forceUpdate();
props.getSpreadSheet?.(s2Ref.current);
}, [props, renderSpreadSheet, setLoading, forceUpdate]);
// init
React.useEffect(() => {
buildSpreadSheet();
return () => {
s2Ref.current.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// 重渲 effect:dataCfg, options or theme changed
useUpdateEffect(() => {
const [prevDataCfg, prevOptions, prevThemeCfg] = updatePrevDepsRef.current;
updatePrevDepsRef.current = [dataCfg, options, themeCfg];
let reloadData = false;
let reBuildDataSet = false;
if (!Object.is(prevDataCfg, dataCfg)) {
reloadData = true;
s2Ref.current?.setDataCfg(dataCfg);
}
if (!Object.is(prevOptions, options)) {
if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) {
// 自定义树目录需要重新构建 CustomTreePivotDataSet
reBuildDataSet = true;
reloadData = true;
s2Ref.current?.setDataCfg(dataCfg);
}
s2Ref.current?.setOptions(options);
s2Ref.current?.changeSheetSize(options.width, options.height);
}
if (!Object.is(prevThemeCfg, themeCfg)) {
s2Ref.current?.setThemeCfg(themeCfg);
}
/**
* onSheetUpdate 交出控制权
* 由传入方决定最终的 render 模式
*/
const renderOptions = onSheetUpdate({
reloadData,
reBuildDataSet,
});
s2Ref.current?.render(renderOptions.reloadData, {
reBuildDataSet: renderOptions.reBuildDataSet,
});
}, [dataCfg, options, themeCfg, onSheetUpdate]);
useResize({
s2: s2Ref.current,
container: containerRef.current,
wrapper: wrapperRef.current,
adaptive: props.adaptive,
});
return {
s2Ref,
containerRef,
wrapperRef,
loading,
setLoading,
pagination,
};
}