-
Notifications
You must be signed in to change notification settings - Fork 268
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds option value decoder utility functions (#2425)
Co-authored-by: Aaron Richner <[email protected]> Co-authored-by: Helen Lin <[email protected]>
- Loading branch information
1 parent
8337e53
commit 76cd4f9
Showing
11 changed files
with
549 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@shopify/hydrogen': patch | ||
'@shopify/hydrogen-react': patch | ||
--- | ||
|
||
Add utility functions `decodeEncodedVariant` and `isOptionValueCombinationInEncodedVariant` for parsing `product.encodedVariantExistence` and `product.encodedVariantAvailability` fields. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
packages/hydrogen-react/src/optionValueDecoder.decodeEncodedVariant.doc.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; | ||
|
||
const data: ReferenceEntityTemplateSchema = { | ||
name: 'decodeEncodedVariant', | ||
category: 'utilities', | ||
isVisualComponent: false, | ||
related: [ | ||
{ | ||
name: 'isOptionValueCombinationInEncodedVariant', | ||
type: 'utility', | ||
url: '/docs/api/hydrogen/2024-10/utilities/isOptionValueCombinationInEncodedVariant', | ||
}, | ||
], | ||
description: | ||
'Decodes an encoded option value string into an array of option value combinations.', | ||
type: 'utility', | ||
defaultExample: { | ||
description: 'Decode an encoded option value string', | ||
codeblock: { | ||
tabs: [ | ||
{ | ||
title: 'JavaScript', | ||
code: './optionValueDecoder.decodeEncodedVariant.example.js', | ||
language: 'js', | ||
}, | ||
], | ||
title: 'Example code', | ||
}, | ||
}, | ||
definitions: [ | ||
{ | ||
title: 'Props', | ||
type: 'DecodeEncodedVariantGeneratedType', | ||
description: '', | ||
}, | ||
], | ||
}; | ||
|
||
export default data; |
34 changes: 34 additions & 0 deletions
34
packages/hydrogen-react/src/optionValueDecoder.decodeEncodedVariant.example.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import {decodeEncodedVariant} from '@shopify/hydrogen-react'; | ||
|
||
// product.options = [ | ||
// { | ||
// name: 'Color', | ||
// optionValues: [ | ||
// {name: 'Red'}, | ||
// {name: 'Blue'}, | ||
// {name: 'Green'}, | ||
// ] | ||
// }, | ||
// { | ||
// name: 'Size', | ||
// optionValues: [ | ||
// {name: 'S'}, | ||
// {name: 'M'}, | ||
// {name: 'L'}, | ||
// ] | ||
// } | ||
// ] | ||
|
||
const encodedVariantAvailability = 'v1_0:0-2,1:2,'; | ||
|
||
const decodedVariantAvailability = decodeEncodedVariant( | ||
encodedVariantAvailability, | ||
); | ||
|
||
// decodedVariantAvailability | ||
// { | ||
// [0,0], // Red, S | ||
// [0,1], // Red, M | ||
// [0,2], // Red, L | ||
// [1,2] // Blue, L | ||
// } |
40 changes: 40 additions & 0 deletions
40
...ges/hydrogen-react/src/optionValueDecoder.isOptionValueCombinationInEncodedVariant.doc.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; | ||
|
||
const data: ReferenceEntityTemplateSchema = { | ||
name: 'isOptionValueCombinationInEncodedVariant', | ||
category: 'utilities', | ||
isVisualComponent: false, | ||
related: [ | ||
{ | ||
name: 'decodeEncodedVariant', | ||
type: 'utility', | ||
url: '/docs/api/hydrogen/2024-10/utilities/decodeEncodedVariant', | ||
}, | ||
], | ||
description: ` | ||
Determines whether an option value combination is present in an encoded option value string.\n\n\`targetOptionValueCombination\` - Indices of option values to look up in the encoded option value string. A partial set of indices may be passed to determine whether a node or any children is present. For example, if a product has 3 options, passing \`[0]\` will return true if any option value combination for the first option's option value is present in the encoded string. | ||
`, | ||
type: 'utility', | ||
defaultExample: { | ||
description: 'Check if option value is in encoding', | ||
codeblock: { | ||
tabs: [ | ||
{ | ||
title: 'JavaScript', | ||
code: './optionValueDecoder.isOptionValueCombinationInEncodedVariant.example.js', | ||
language: 'js', | ||
}, | ||
], | ||
title: 'Example code', | ||
}, | ||
}, | ||
definitions: [ | ||
{ | ||
title: 'Props', | ||
type: 'IsOptionValueCombinationInEncodedVariantForDocs', | ||
description: '', | ||
}, | ||
], | ||
}; | ||
|
||
export default data; |
45 changes: 45 additions & 0 deletions
45
...hydrogen-react/src/optionValueDecoder.isOptionValueCombinationInEncodedVariant.example.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import {isOptionValueCombinationInEncodedVariant} from '@shopify/hydrogen-react'; | ||
|
||
// product.options = [ | ||
// { | ||
// name: 'Color', | ||
// optionValues: [ | ||
// {name: 'Red'}, | ||
// {name: 'Blue'}, | ||
// {name: 'Green'}, | ||
// ] | ||
// }, | ||
// { | ||
// name: 'Size', | ||
// optionValues: [ | ||
// {name: 'S'}, | ||
// {name: 'M'}, | ||
// {name: 'L'}, | ||
// ] | ||
// } | ||
// ] | ||
const encodedVariantExistence = 'v1_0:0-1,1:2,'; | ||
|
||
// For reference: decoded encodedVariantExistence | ||
// { | ||
// [0,0], // Red, S | ||
// [0,1], // Red, M | ||
// [1,2] // Blue, L | ||
// } | ||
|
||
// Returns true since there are variants exist for [Red] | ||
isOptionValueCombinationInEncodedVariant([0], encodedVariantExistence); // true | ||
|
||
isOptionValueCombinationInEncodedVariant([0, 0], encodedVariantExistence); // true | ||
isOptionValueCombinationInEncodedVariant([0, 1], encodedVariantExistence); // true | ||
isOptionValueCombinationInEncodedVariant([0, 2], encodedVariantExistence); // false - no variant exist for [Red, L] | ||
|
||
// Returns true since there is a variant exist for [Blue] | ||
isOptionValueCombinationInEncodedVariant([1], encodedVariantExistence); // true | ||
|
||
isOptionValueCombinationInEncodedVariant([1, 0], encodedVariantExistence); // false - no variant exist for [Blue, S] | ||
isOptionValueCombinationInEncodedVariant([1, 1], encodedVariantExistence); // false - no variant exist for [Blue, M] | ||
isOptionValueCombinationInEncodedVariant([1, 2], encodedVariantExistence); // true | ||
|
||
// Returns false since there is no variant exist for [Green] | ||
isOptionValueCombinationInEncodedVariant([2], encodedVariantExistence); // false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import {describe, expect, it} from 'vitest'; | ||
import { | ||
decodeEncodedVariant, | ||
isOptionValueCombinationInEncodedVariant, | ||
} from './optionValueDecoder.js'; | ||
|
||
describe('isOptionValueCombinationInEncodedVariant', () => { | ||
it('returns true when target option values are present in encoded option values', () => { | ||
const MOCK_ENCODED_OPTION_VALUES = 'v1_0:0:0,,1:1:1,,2:2:2,,'; | ||
|
||
expect( | ||
isOptionValueCombinationInEncodedVariant( | ||
[0, 0, 0], | ||
MOCK_ENCODED_OPTION_VALUES, | ||
), | ||
).toBe(true); | ||
}); | ||
|
||
it('returns true when a partial target option value is present in encoded option values', () => { | ||
const MOCK_ENCODED_OPTION_VALUES = 'v1_0:0:0,,1:1:1,,2:2:2,,'; | ||
|
||
expect( | ||
isOptionValueCombinationInEncodedVariant([0], MOCK_ENCODED_OPTION_VALUES), | ||
).toBe(true); | ||
|
||
expect( | ||
isOptionValueCombinationInEncodedVariant( | ||
[2, 2], | ||
MOCK_ENCODED_OPTION_VALUES, | ||
), | ||
).toBe(true); | ||
}); | ||
|
||
it('returns false when target option values are not present in encoded option values', () => { | ||
const MOCK_ENCODED_OPTION_VALUES = 'v1_0:0:0,,1:1:1,,2:2:2,,'; | ||
|
||
expect( | ||
isOptionValueCombinationInEncodedVariant( | ||
[0, 0, 1], | ||
MOCK_ENCODED_OPTION_VALUES, | ||
), | ||
).toBe(false); | ||
}); | ||
|
||
it('returns false if no target option values are passed', () => { | ||
const MOCK_ENCODED_OPTION_VALUES = 'v1_0:0:0,,1:1:1,,2:2:2,,'; | ||
|
||
expect( | ||
isOptionValueCombinationInEncodedVariant([], MOCK_ENCODED_OPTION_VALUES), | ||
).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('decodeEncodedVariant', () => { | ||
describe('v1', () => { | ||
const VERSION_PREFIX = 'v1_'; | ||
|
||
it('it correctly decodes a set of 1-dimensional arrays with no gaps in the number sequence', () => { | ||
expect(decodeEncodedVariant(`${VERSION_PREFIX}0-9`)).toStrictEqual([ | ||
[0], | ||
[1], | ||
[2], | ||
[3], | ||
[4], | ||
[5], | ||
[6], | ||
[7], | ||
[8], | ||
[9], | ||
]); | ||
}); | ||
|
||
it('it correctly decodes a set of 1-dimensional arrays with gaps in the number sequence', () => { | ||
expect( | ||
decodeEncodedVariant(`${VERSION_PREFIX}0-2 4-6 8-9`), | ||
).toStrictEqual([[0], [1], [2], [4], [5], [6], [8], [9]]); | ||
}); | ||
|
||
it('it correctly decodes a set of 2-dimensional arrays with no gaps in the number sequence', () => { | ||
expect( | ||
decodeEncodedVariant(`${VERSION_PREFIX}0:0-2,1:0-2,2:0-2,`), | ||
).toStrictEqual([ | ||
[0, 0], | ||
[0, 1], | ||
[0, 2], | ||
[1, 0], | ||
[1, 1], | ||
[1, 2], | ||
[2, 0], | ||
[2, 1], | ||
[2, 2], | ||
]); | ||
}); | ||
|
||
it('it correctly decodes a set of 2-dimensional arrays with gaps in the number sequence', () => { | ||
expect( | ||
decodeEncodedVariant(`${VERSION_PREFIX}0:0-1,1:0 2,2:1-2,`), | ||
).toStrictEqual([ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[1, 2], | ||
[2, 1], | ||
[2, 2], | ||
]); | ||
}); | ||
|
||
it('it correctly decodes a set of 3-dimensional arrays with no gaps in the number sequence', () => { | ||
const output = generateCombinations(3, 3); | ||
expect( | ||
decodeEncodedVariant( | ||
`${VERSION_PREFIX}0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-2,,`, | ||
), | ||
).toStrictEqual(output); | ||
}); | ||
|
||
it('it correctly decodes a set of 3-dimensional arrays with gaps in the number sequence', () => { | ||
const output = generateCombinations(3, 3, [ | ||
[1, 0, 1], | ||
[2, 2, 2], | ||
[0, 2, 2], | ||
]); | ||
expect( | ||
decodeEncodedVariant( | ||
`${VERSION_PREFIX}0:0:0-2,1:0-2,2:0-1,,1:0:0 2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-1,`, | ||
), | ||
).toStrictEqual(output); | ||
}); | ||
|
||
it('it correctly decodes a set of 4-dimensional arrays with no gaps in the number sequence', () => { | ||
const output = generateCombinations(4, 3); | ||
expect( | ||
decodeEncodedVariant( | ||
`${VERSION_PREFIX}"0:0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-2,,,1:0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-2,,,2:0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-2,,,"`, | ||
), | ||
).toStrictEqual(output); | ||
}); | ||
|
||
it('it correctly decodes a set of 4-dimensional arrays with gaps in the number sequence', () => { | ||
const output = generateCombinations(4, 3, [ | ||
[1, 0, 1, 0], | ||
[2, 2, 2, 0], | ||
[0, 2, 2, 1], | ||
]); | ||
expect( | ||
decodeEncodedVariant( | ||
`${VERSION_PREFIX}0:0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0 2,,,1:0:0:0-2,1:1-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:0-2,,,2:0:0:0-2,1:0-2,2:0-2,,1:0:0-2,1:0-2,2:0-2,,2:0:0-2,1:0-2,2:1-2,,,`, | ||
), | ||
).toStrictEqual(output); | ||
}); | ||
}); | ||
}); | ||
|
||
// generate all possible option value combos of a given depth and width | ||
function generateCombinations( | ||
depth: number, | ||
width: number, | ||
exclusions: number[][] = [], | ||
): number[][] { | ||
const input: number[][] = []; | ||
|
||
function isInExclusions(array: number[]): boolean { | ||
return exclusions.some( | ||
(exclusion) => | ||
exclusion.length === array.length && | ||
exclusion.every((value, index) => value === array[index]), | ||
); | ||
} | ||
|
||
function generate(currentArray: number[]): void { | ||
if (currentArray.length === depth) { | ||
if (!isInExclusions(currentArray)) { | ||
input.push([...currentArray]); | ||
} | ||
return; | ||
} | ||
|
||
for (let i = 0; i < width; i++) { | ||
currentArray.push(i); | ||
generate(currentArray); | ||
currentArray.pop(); | ||
} | ||
} | ||
|
||
generate([]); | ||
return input; | ||
} |
Oops, something went wrong.