From 206d143a9ba28cb36527134bcff5aee47f09aa4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:59:19 +0000 Subject: [PATCH 01/11] Bump follow-redirects from 1.13.3 to 1.14.7 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.13.3 to 1.14.7. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.13.3...v1.14.7) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6878c42..78dabf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1304,9 +1304,9 @@ find-up@^3.0.0: locate-path "^3.0.0" follow-redirects@^1.0.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== for-in@^1.0.2: version "1.0.2" From a6ff02312bb054392c8581f176c59e6296db979d Mon Sep 17 00:00:00 2001 From: Denis Strelkov Date: Fri, 4 Feb 2022 14:17:32 +0300 Subject: [PATCH 02/11] Update functions to get matching terms for more precise search --- example/Demo.res | 8 +++ src/Demos.res | 2 +- src/ReshowcaseUi.res | 2 +- src/ReshowcaseUi__HighlightTerms.res | 32 ++++++--- src/tests/HighlightTermsTest.res | 103 ++++++++++++++++++++++++++- 5 files changed, 132 insertions(+), 15 deletions(-) diff --git a/example/Demo.res b/example/Demo.res index 456c336..0022182 100644 --- a/example/Demo.res +++ b/example/Demo.res @@ -119,4 +119,12 @@ start() ) }) +demo(({addDemo: _, addCategory}) => { + addCategory("Test search", ({addDemo, addCategory: _}) => { + addDemo("OneTwoThreeFour", _ => React.null) + addDemo("OneTwoThreeFive", _ => React.null) + addDemo("OneTwoFourSeven", _ => React.null) + }) +}) + start() diff --git a/src/Demos.res b/src/Demos.res index 71f521e..5014e13 100644 --- a/src/Demos.res +++ b/src/Demos.res @@ -47,7 +47,7 @@ let rec isNestedEntityMatchSearch = (demos: t, searchString) => { ->Js.Dict.entries ->Array.some(((entityName, entity)) => { let isEntityNameMatchSearch = - HighlightTerms.getMatchingTerms(searchString, ~entityName)->Array.size > 0 + HighlightTerms.getMatchingTerms(~searchString, ~entityName)->Array.size > 0 switch entity { | Demo(_) => isEntityNameMatchSearch | Category(demos) => isEntityNameMatchSearch || isNestedEntityMatchSearch(demos, searchString) diff --git a/src/ReshowcaseUi.res b/src/ReshowcaseUi.res index fd418d0..957f462 100644 --- a/src/ReshowcaseUi.res +++ b/src/ReshowcaseUi.res @@ -252,7 +252,7 @@ module DemoListSidebar = { demos ->Array.map(((entityName, entity)) => { - let searchMatchingTerms = HighlightTerms.getMatchingTerms(searchString, ~entityName) + let searchMatchingTerms = HighlightTerms.getMatchingTerms(~searchString, ~entityName) let isEntityNameMatchSearch = searchString == "" || searchMatchingTerms->Belt.Array.size > 0 switch entity { | Demo(_) => diff --git a/src/ReshowcaseUi__HighlightTerms.res b/src/ReshowcaseUi__HighlightTerms.res index 6248963..000e744 100644 --- a/src/ReshowcaseUi__HighlightTerms.res +++ b/src/ReshowcaseUi__HighlightTerms.res @@ -7,29 +7,39 @@ type textPart = Marked(string) | Unmarked(string) type termPosition = Start | Middle | End -let getMatchingTerms = (searchString, ~entityName) => { +let getTermGroups = (~searchString, ~entityName) => { switch searchString { | "" => [] | _ => + let searchString = searchString->String.toLowerCase let entityName = entityName->String.toLowerCase + if entityName->String.includes(searchString) { - [searchString] + [[searchString]] } else { - let terms = + + let refinedSearchString = searchString ->String.replaceByRe(%re("/\\s+/g"), " ") - ->String.splitByRe(%re("/( |, |,)/")) - ->Belt.Array.keepMap(s => - switch s { - | None => None - | Some(s) => String.length(s) > 1 ? Some(s) : None // filter out meaningless one-char terms - } - ) - terms->Array.filter(term => Js.String2.includes(entityName, term)) + ->String.replaceByRe(%re("/( , |, | ,)/g"), ",") + + refinedSearchString + ->String.split(",") + ->Array.map(s => s->String.split(" ")) + ->Array.map(arr => arr->Belt.Array.keepMap(s => String.length(s) > 1 ? Some(s) : None)) + // filter out meaningless one-char terms } } } +let getMatchingTerms = (~searchString, ~entityName) => { + let entityName = entityName->String.toLowerCase + let termGroups = getTermGroups(~searchString, ~entityName) + let includedTerms = + termGroups->Array.filter(terms => terms->Array.every(term => String.includes(entityName, term))) + Belt.Array.concatMany(includedTerms) +} + let getMarkRangeIndexes = (text, substring) => { let indexFrom = String.indexOf(String.toLowerCase(text), String.toLowerCase(substring)) let indexTo = indexFrom + String.length(substring) diff --git a/src/tests/HighlightTermsTest.res b/src/tests/HighlightTermsTest.res index d99c698..a9c15b4 100644 --- a/src/tests/HighlightTermsTest.res +++ b/src/tests/HighlightTermsTest.res @@ -18,7 +18,7 @@ let isEqual = (~msg="", v1, v2) => { } } -let test = () => { +let getTextPartsTest = () => { isEqual( ~msg="Term at the start", HighlightTerms.getTextParts(~text="OneTwoThreeFourFive", ~terms=["One"]), @@ -62,4 +62,103 @@ let test = () => { ) } -test() +let getTermGroupsTest = () => { + isEqual( + ~msg="Empty search string, no groups", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString=""), + [], + ) + + isEqual( + ~msg="The same term as entity name is a group with a single item", + HighlightTerms.getTermGroups( + ~entityName="OneTwoThreeFourFive", + ~searchString="onetwothreefourfive", + ), + [["onetwothreefourfive"]], + ) + + isEqual( + ~msg="Two terms with space is a single group with two terms", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString="one two"), + [["one", "two"]], + ) + + isEqual( + ~msg="Two terms with comma are two groups.", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString="one,two"), + [["one"], ["two"]], + ) + + isEqual( + ~msg="Three terms with comma (2 and 1)", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString="one two, three"), + [["one", "two"], ["three"]], + ) + + isEqual( + ~msg="Three terms with comma (1 and 2)", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString="one, two three"), + [["one"], ["two", "three"]], + ) + + isEqual( + ~msg="One letter term is filtered out", + HighlightTerms.getTermGroups(~entityName="OneTwoThreeFourFive", ~searchString="one t"), + [["one"]], + ) +} + +let getMatchingTermsTest = () => { + // If we use spaces between terms we expect all of them to be included to get more precise search results. + // If we use commas between terms we expect terms from one or another group to be included. + + isEqual( + ~msg="One exact term match", + HighlightTerms.getMatchingTerms( + ~entityName="OneTwoThreeFourFive", + ~searchString="onetwothreefourfive", + ), + ["onetwothreefourfive"], + ) + + isEqual( + ~msg="Single term match", + HighlightTerms.getMatchingTerms(~entityName="OneTwoThreeFourFive", ~searchString="one"), + ["one"], + ) + + isEqual( + ~msg="Two required terms match", + HighlightTerms.getMatchingTerms(~entityName="OneTwoThreeFourFive", ~searchString="one two"), + ["one", "two"], + ) + + isEqual( + ~msg="Two required terms or one required term, all match", + HighlightTerms.getMatchingTerms( + ~entityName="OneTwoThreeFourFive", + ~searchString="one two, three", + ), + ["one", "two", "three"], + ) + + isEqual( + ~msg="Two terms, the alternative term is missing, one match", + HighlightTerms.getMatchingTerms( + ~entityName="OneTwoThreeFourFive", + ~searchString="missing, one", + ), + ["one"], + ) + + isEqual( + ~msg="Two required terms, one of them is missing, no match", + HighlightTerms.getMatchingTerms(~entityName="OneTwoThreeFourFive", ~searchString="one missing"), + [], + ) +} + +getTextPartsTest() +getTermGroupsTest() +getMatchingTermsTest() From 67c5eba3a9e4f93157ff916ed9a1134b43f37584 Mon Sep 17 00:00:00 2001 From: Denis Strelkov Date: Fri, 4 Feb 2022 15:47:13 +0300 Subject: [PATCH 03/11] Use separate html templates for ui and demo --- commands/reshowcase | 9 +++++++-- example/template.html | 15 +++++++++++++++ package.json | 4 ++-- src/ReshowcaseUi.res | 2 +- src/demo-template.html | 15 +++++++++++++++ src/{index.html => ui-template.html} | 0 6 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 example/template.html create mode 100644 src/demo-template.html rename src/{index.html => ui-template.html} (100%) diff --git a/commands/reshowcase b/commands/reshowcase index b028340..310d550 100755 --- a/commands/reshowcase +++ b/commands/reshowcase @@ -88,6 +88,11 @@ const compiler = webpack({ patterns: [{ from: path.join(__dirname, "../src/favicon.png"), to: "" }], }), new HtmlWebpackPlugin({ + filename: "index.html", + template: path.join(__dirname, "../src/ui-template.html"), + }), + new HtmlWebpackPlugin({ + filename: "demo.html", template: process.argv.find((item) => item.startsWith("--template=")) ? path.join( process.cwd(), @@ -95,7 +100,7 @@ const compiler = webpack({ .find((item) => item.startsWith("--template=")) .replace(/--template=/, "") ) - : path.join(__dirname, "../src/index.html"), + : path.join(__dirname, "../src/demo-template.html"), }), ], }); @@ -127,7 +132,7 @@ if (isBuild) { index: "/index.html", }, stats: "errors-only", - ...(config.devServer || {}) + ...(config.devServer || {}), }); if (config.devServer && config.devServer.socket) { diff --git a/example/template.html b/example/template.html new file mode 100644 index 0000000..a39079c --- /dev/null +++ b/example/template.html @@ -0,0 +1,15 @@ + + + + + + Reshowcase custom demo template + + + + + +
+ + + diff --git a/package.json b/package.json index 31bebd1..bc2e28e 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build-bsb": "bsb -make-world", "clean": "bsb -clean-world", "start-bsb": "bsb -make-world -w", - "start": "bsb -clean-world && bsb -make-world; concurrently --names 'wp,bs' -c 'bgBlue.bold,bgGreen.bold' 'bsb -make-world -w' './commands/reshowcase start --entry=./example/Demo.bs.js'", - "build": "bsb -clean-world && bsb -make-world && rm -r build ; ./commands/reshowcase build --entry=./example/Demo.bs.js --output=./build", + "start": "bsb -clean-world && bsb -make-world; concurrently --names 'wp,bs' -c 'bgBlue.bold,bgGreen.bold' 'bsb -make-world -w' './commands/reshowcase start --entry=./example/Demo.bs.js --template=./example/template.html'", + "build": "bsb -clean-world && bsb -make-world && rm -r build ; ./commands/reshowcase build --entry=./example/Demo.bs.js --template=./example/template.html --output=./build", "test": "node ./lib/js/src/tests/HighlightTermsTest.bs.js" }, "keywords": [ diff --git a/src/ReshowcaseUi.res b/src/ReshowcaseUi.res index fd418d0..e831a34 100644 --- a/src/ReshowcaseUi.res +++ b/src/ReshowcaseUi.res @@ -724,7 +724,7 @@ module DemoUnitFrame = { let window = iframe["contentWindow"] onLoad(window) }} - src={`?iframe=true&${queryString}`} + src={`/demo.html?iframe=true&${queryString}`} style={ReactDOM.Style.make( ~height={ switch responsiveMode { diff --git a/src/demo-template.html b/src/demo-template.html new file mode 100644 index 0000000..da7767b --- /dev/null +++ b/src/demo-template.html @@ -0,0 +1,15 @@ + + + + + + Reshowcase demo + + + + + +
+ + + diff --git a/src/index.html b/src/ui-template.html similarity index 100% rename from src/index.html rename to src/ui-template.html From b34b9455f7525beab9bc97595c8f25e615026dda Mon Sep 17 00:00:00 2001 From: Denis Strelkov Date: Fri, 4 Feb 2022 19:32:40 +0300 Subject: [PATCH 04/11] Add setting to disable clean iframe url --- commands/reshowcase | 22 +++++++++++++++++++++- package.json | 4 ++-- src/ReshowcaseUi.res | 5 ++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/commands/reshowcase b/commands/reshowcase index 310d550..e07f350 100755 --- a/commands/reshowcase +++ b/commands/reshowcase @@ -43,6 +43,23 @@ if (task !== "build" && task !== "start") { process.exit(1); } +// Servers can handle paths to HTML files differently. +// Allow using full path to HTML template in "src" attribute of iframe in case of possible issues. +const useFullframeUrl = (() => { + const prefix = `--full-iframe-url=`; + const arg = process.argv.find((item) => item.startsWith(prefix)); + if (arg === undefined) { + return false; + } else { + const value = arg.replace(prefix, ""); + if (value === "true") { + return true; + } else { + return false; + } + } +})(); + const isBuild = task === "build"; const entryPath = getRequiredPathValue("entry"); @@ -92,7 +109,7 @@ const compiler = webpack({ template: path.join(__dirname, "../src/ui-template.html"), }), new HtmlWebpackPlugin({ - filename: "demo.html", + filename: "./demo/index.html", template: process.argv.find((item) => item.startsWith("--template=")) ? path.join( process.cwd(), @@ -102,6 +119,9 @@ const compiler = webpack({ ) : path.join(__dirname, "../src/demo-template.html"), }), + new webpack.DefinePlugin({ + USE_FULL_IFRAME_URL: JSON.stringify(useFullframeUrl), + }), ], }); diff --git a/package.json b/package.json index bc2e28e..2140d7f 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build-bsb": "bsb -make-world", "clean": "bsb -clean-world", "start-bsb": "bsb -make-world -w", - "start": "bsb -clean-world && bsb -make-world; concurrently --names 'wp,bs' -c 'bgBlue.bold,bgGreen.bold' 'bsb -make-world -w' './commands/reshowcase start --entry=./example/Demo.bs.js --template=./example/template.html'", - "build": "bsb -clean-world && bsb -make-world && rm -r build ; ./commands/reshowcase build --entry=./example/Demo.bs.js --template=./example/template.html --output=./build", + "start": "bsb -clean-world && bsb -make-world; concurrently --names 'wp,bs' -c 'bgBlue.bold,bgGreen.bold' 'bsb -make-world -w' './commands/reshowcase start --entry=./example/Demo.bs.js --template=./example/template.html --full-iframe-url=false'", + "build": "bsb -clean-world && bsb -make-world && rm -r build ; ./commands/reshowcase build --entry=./example/Demo.bs.js --template=./example/template.html --output=./build --full-iframe-url=false", "test": "node ./lib/js/src/tests/HighlightTermsTest.bs.js" }, "keywords": [ diff --git a/src/ReshowcaseUi.res b/src/ReshowcaseUi.res index e831a34..3a14a08 100644 --- a/src/ReshowcaseUi.res +++ b/src/ReshowcaseUi.res @@ -715,8 +715,11 @@ module DemoUnitFrame = { (), ) + let useFullframeUrl: bool = %raw(`typeof USE_FULL_IFRAME_URL === "boolean" ? USE_FULL_IFRAME_URL : false`) + @react.component let make = (~queryString: string, ~responsiveMode, ~onLoad: Js.t<'a> => unit) => { + let iframePath = useFullframeUrl ? "/demo/index.html" : "/demo"