diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /layout/style/test/flexbox_layout_testcases.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/style/test/flexbox_layout_testcases.js')
-rw-r--r-- | layout/style/test/flexbox_layout_testcases.js | 1317 |
1 files changed, 1317 insertions, 0 deletions
diff --git a/layout/style/test/flexbox_layout_testcases.js b/layout/style/test/flexbox_layout_testcases.js new file mode 100644 index 0000000000..c8990877ab --- /dev/null +++ b/layout/style/test/flexbox_layout_testcases.js @@ -0,0 +1,1317 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 sw=2 sts=2 et: */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * For the purposes of this test, flex items are specified as a hash with a + * hash-entry for each CSS property that is to be set. In these per-property + * entries, the key is the property-name, and the value can be either of the + * following: + * (a) the property's specified value (which indicates that we don't need to + * bother checking the computed value of this particular property) + * ...OR... + * (b) an array with 2-3 entries... + * [specifiedValue, expectedComputedValue (, epsilon) ] + * ...which indicates that the property's computed value should be + * checked. The array's first entry (for the specified value) may be + * null; this means that no value should be explicitly specified for this + * property. The second entry is the property's expected computed + * value. The third (optional) entry is an epsilon value, which allows for + * fuzzy equality when testing the computed value. + * + * To allow these testcases to be re-used in both horizontal and vertical + * flex containers, we specify "width"/"min-width"/etc. using the aliases + * "_main-size", "_min-main-size", etc. The test code can map these + * placeholder names to their corresponding property-names using the maps + * defined below -- gRowPropertyMapping, gColumnPropertyMapping, etc. + * + * If the testcase needs to customize its flex container at all (e.g. by + * specifying a custom container-size), it can do so by including a hash + * called "container_properties", with propertyName:propertyValue mappings. + * (This hash can use aliased property-names like "_main-size" as well.) + */ + +// The standard main-size we'll use for our flex container when setting up +// the testcases defined below: +var gDefaultFlexContainerSize = "200px"; + +// Left-to-right versions of placeholder property-names used in +// testcases below: +var gRowPropertyMapping = { + "_main-size": "width", + "_min-main-size": "min-width", + "_max-main-size": "max-width", + "_border-main-start-width": "border-left-width", + "_border-main-end-width": "border-right-width", + "_padding-main-start": "padding-left", + "_padding-main-end": "padding-right", + "_margin-main-start": "margin-left", + "_margin-main-end": "margin-right", +}; + +// Right-to-left versions of placeholder property-names used in +// testcases below: +var gRowReversePropertyMapping = { + "_main-size": "width", + "_min-main-size": "min-width", + "_max-main-size": "max-width", + "_border-main-start-width": "border-right-width", + "_border-main-end-width": "border-left-width", + "_padding-main-start": "padding-right", + "_padding-main-end": "padding-left", + "_margin-main-start": "margin-right", + "_margin-main-end": "margin-left", +}; + +// Top-to-bottom versions of placeholder property-names used in +// testcases below: +var gColumnPropertyMapping = { + "_main-size": "height", + "_min-main-size": "min-height", + "_max-main-size": "max-height", + "_border-main-start-width": "border-top-width", + "_border-main-end-width": "border-bottom-width", + "_padding-main-start": "padding-top", + "_padding-main-end": "padding-bottom", + "_margin-main-start": "margin-top", + "_margin-main-end": "margin-bottom", +}; + +// Bottom-to-top versions of placeholder property-names used in +// testcases below: +var gColumnReversePropertyMapping = { + "_main-size": "height", + "_min-main-size": "min-height", + "_max-main-size": "max-height", + "_border-main-start-width": "border-bottom-width", + "_border-main-end-width": "border-top-width", + "_padding-main-start": "padding-bottom", + "_padding-main-end": "padding-top", + "_margin-main-start": "margin-bottom", + "_margin-main-end": "margin-top", +}; + +// The list of actual testcase definitions: +var gFlexboxTestcases = [ + // No flex properties specified --> should just use 'width' for sizing + { + items: [ + { "_main-size": ["40px", "40px"] }, + { "_main-size": ["65px", "65px"] }, + ], + }, + // flex-basis is specified: + { + items: [ + { "flex-basis": "50px", "_main-size": [null, "50px"] }, + { + "flex-basis": "20px", + "_main-size": [null, "20px"], + }, + ], + }, + // flex-basis is *large* -- sum of flex-basis values is > flex container size: + // (w/ 0 flex-shrink so we don't shrink): + { + items: [ + { + flex: "0 0 150px", + "_main-size": [null, "150px"], + }, + { + flex: "0 0 90px", + "_main-size": [null, "90px"], + }, + ], + }, + // flex-basis is *large* -- each flex-basis value is > flex container size: + // (w/ 0 flex-shrink so we don't shrink): + { + items: [ + { + flex: "0 0 250px", + "_main-size": [null, "250px"], + }, + { + flex: "0 0 400px", + "_main-size": [null, "400px"], + }, + ], + }, + // flex-basis has percentage value: + { + items: [ + { + "flex-basis": "30%", + "_main-size": [null, "60px"], + }, + { + "flex-basis": "45%", + "_main-size": [null, "90px"], + }, + ], + }, + // flex-basis has calc(percentage) value: + { + items: [ + { + "flex-basis": "calc(20%)", + "_main-size": [null, "40px"], + }, + { + "flex-basis": "calc(80%)", + "_main-size": [null, "160px"], + }, + ], + }, + // flex-basis has calc(percentage +/- length) value: + { + items: [ + { + "flex-basis": "calc(10px + 20%)", + "_main-size": [null, "50px"], + }, + { + "flex-basis": "calc(60% - 1px)", + "_main-size": [null, "119px"], + }, + ], + }, + // flex-grow is specified: + { + items: [ + { + flex: "1", + "_main-size": [null, "60px"], + }, + { + flex: "2", + "_main-size": [null, "120px"], + }, + { + flex: "0 20px", + "_main-size": [null, "20px"], + }, + ], + }, + // Same ratio as prev. testcase; making sure we handle float inaccuracy + { + items: [ + { + flex: "100000", + "_main-size": [null, "60px"], + }, + { + flex: "200000", + "_main-size": [null, "120px"], + }, + { + flex: "0.000001 20px", + "_main-size": [null, "20px"], + }, + ], + }, + // Same ratio as prev. testcase, but with items cycled and w/ + // "flex: none" & explicit size instead of "flex: 0 20px" + { + items: [ + { + flex: "none", + "_main-size": ["20px", "20px"], + }, + { + flex: "1", + "_main-size": [null, "60px"], + }, + { + flex: "2", + "_main-size": [null, "120px"], + }, + ], + }, + + // ...and now with flex-grow:[huge] to be sure we handle infinite float values + // gracefully. + { + items: [ + { + flex: "9999999999999999999999999999999999999999999999999999999", + "_main-size": [null, "200px"], + }, + ], + }, + { + items: [ + { + flex: "9999999999999999999999999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "9999999999999999999999999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "9999999999999999999999999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "9999999999999999999999999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + ], + }, + { + items: [ + { + flex: "99999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "99999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "99999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + { + flex: "99999999999999999999999999999999999", + "_main-size": [null, "50px"], + }, + ], + }, + + // And now, some testcases to check that we handle float accumulation error + // gracefully. + + // First, a testcase with just a custom-sized huge container, to be sure we'll + // be able to handle content on that scale, in the subsequent more-complex + // testcases: + { + container_properties: { + "_main-size": "9000000px", + }, + items: [ + { + flex: "1", + "_main-size": [null, "9000000px"], + }, + ], + }, + // ...and now with two flex items dividing up that container's huge size: + { + container_properties: { + "_main-size": "9000000px", + }, + items: [ + { + flex: "2", + "_main-size": [null, "6000000px"], + }, + { + flex: "1", + "_main-size": [null, "3000000px"], + }, + ], + }, + + // OK, now to actually test accumulation error. Below, we have six flex items + // splitting up the container's size, with huge differences between flex + // weights. For simplicity, I've set up the weights so that they sum exactly + // to the container's size in px. So 1 unit of flex *should* get you 1px. + // + // NOTE: The expected computed "_main-size" values for the flex items below + // appear to add up to more than their container's size, which would suggest + // that they overflow their container unnecessarily. But they don't actually + // overflow -- this discrepancy is simply because Gecko's code for reporting + // computed-sizes rounds to 6 significant figures (in particular, the method + // (nsTSubstring_CharT::AppendFloat() does this). Internally, in app-units, + // the child frames' main-sizes add up exactly to the container's main-size, + // as you'd hope & expect. + { + container_properties: { + "_main-size": "9000000px", + }, + items: [ + { + flex: "3000000", + "_main-size": [null, "3000000px"], + }, + { + flex: "1", + "_main-size": [null, "1px"], + }, + { + flex: "1", + "_main-size": [null, "1px"], + }, + { + flex: "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + { + flex: "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + { + flex: "1", + "_main-size": [null, "1px", 0.2], + }, + ], + }, + // Same flex items as previous testcase, but now reordered such that the items + // with tiny flex weights are all listed last: + { + container_properties: { + "_main-size": "9000000px", + }, + items: [ + { + flex: "3000000", + "_main-size": [null, "3000000px"], + }, + { + flex: "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + { + flex: "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + { + flex: "1", + "_main-size": [null, "1px", 0.2], + }, + { + flex: "1", + "_main-size": [null, "1px", 0.2], + }, + { + flex: "1", + "_main-size": [null, "1px", 0.2], + }, + ], + }, + // Same flex items as previous testcase, but now reordered such that the items + // with tiny flex weights are all listed first: + { + container_properties: { + "_main-size": "9000000px", + }, + items: [ + { + flex: "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [null, "1px", 0.2], + }, + { + flex: "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [null, "1px", 0.2], + }, + { + flex: "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [null, "1px", 0.2], + }, + { + flex: "3000000", + "_main-size": [null, "3000000px"], + }, + { + flex: "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + { + flex: "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [null, "3000000px"], + }, + ], + }, + + // Trying "flex: auto" (== "1 1 auto") w/ a mix of flex-grow/flex-basis values + { + items: [ + { + flex: "auto", + "_main-size": [null, "45px"], + }, + { + flex: "2", + "_main-size": [null, "90px"], + }, + { + flex: "20px 1 0", + "_main-size": [null, "65px"], + }, + ], + }, + // Same as previous, but with items cycled & different syntax + { + items: [ + { + flex: "20px", + "_main-size": [null, "65px"], + }, + { + flex: "1", + "_main-size": [null, "45px"], + }, + { + flex: "2", + "_main-size": [null, "90px"], + }, + ], + }, + { + items: [ + { + flex: "2", + "_main-size": [null, "100px"], + border: "0px dashed", + "_border-main-start-width": ["5px", "5px"], + "_border-main-end-width": ["15px", "15px"], + "_margin-main-start": ["22px", "22px"], + "_margin-main-end": ["8px", "8px"], + }, + { + flex: "1", + "_main-size": [null, "50px"], + "_margin-main-start": ["auto", "0px"], + "_padding-main-end": ["auto", "0px"], + }, + ], + }, + // Test negative flexibility: + + // Basic testcase: just 1 item (relying on initial "flex-shrink: 1") -- + // should shrink to container size. + { + items: [{ "_main-size": ["400px", "200px"] }], + }, + // ...and now with a "flex" specification and a different flex-shrink value: + { + items: [ + { + flex: "4 2 250px", + "_main-size": [null, "200px"], + }, + ], + }, + // ...and now with multiple items, which all shrink proportionally (by 50%) + // to fit to the container, since they have the same (initial) flex-shrink val + { + items: [ + { "_main-size": ["80px", "40px"] }, + { "_main-size": ["40px", "20px"] }, + { "_main-size": ["30px", "15px"] }, + { "_main-size": ["250px", "125px"] }, + ], + }, + // ...and now with positive flexibility specified. (should have no effect, so + // everything still shrinks by the same proportion, since the flex-shrink + // values are all the same). + { + items: [ + { + flex: "4 3 100px", + "_main-size": [null, "80px"], + }, + { + flex: "5 3 50px", + "_main-size": [null, "40px"], + }, + { + flex: "0 3 100px", + "_main-size": [null, "80px"], + }, + ], + }, + // ...and now with *different* flex-shrink values: + { + items: [ + { + flex: "4 2 50px", + "_main-size": [null, "30px"], + }, + { + flex: "5 3 50px", + "_main-size": [null, "20px"], + }, + { + flex: "0 0 150px", + "_main-size": [null, "150px"], + }, + ], + }, + // Same ratio as prev. testcase; making sure we handle float inaccuracy + { + items: [ + { + flex: "4 20000000 50px", + "_main-size": [null, "30px"], + }, + { + flex: "5 30000000 50px", + "_main-size": [null, "20px"], + }, + { + flex: "0 0.0000001 150px", + "_main-size": [null, "150px"], + }, + ], + }, + // Another "different flex-shrink values" testcase: + { + items: [ + { + flex: "4 2 115px", + "_main-size": [null, "69px"], + }, + { + flex: "5 1 150px", + "_main-size": [null, "120px"], + }, + { + flex: "1 4 30px", + "_main-size": [null, "6px"], + }, + { + flex: "1 0 5px", + "_main-size": [null, "5px"], + }, + ], + }, + + // ...and now with min-size (clamping the effects of flex-shrink on one item): + { + items: [ + { + flex: "4 5 75px", + "_min-main-size": "50px", + "_main-size": [null, "50px"], + }, + { + flex: "5 5 100px", + "_main-size": [null, "62.5px"], + }, + { + flex: "0 4 125px", + "_main-size": [null, "87.5px"], + }, + ], + }, + + // Test a min-size that's much larger than initial preferred size, but small + // enough that our flexed size pushes us over it: + { + items: [ + { + flex: "auto", + "_min-main-size": "110px", + "_main-size": ["50px", "125px"], + }, + { + flex: "auto", + "_main-size": [null, "75px"], + }, + ], + }, + + // Test a min-size that's much larger than initial preferred size, and is + // even larger than our positively-flexed size, so that we have to increase it + // (as a 'min violation') after we've flexed. + { + items: [ + { + flex: "auto", + "_min-main-size": "150px", + "_main-size": ["50px", "150px"], + }, + { + flex: "auto", + "_main-size": [null, "50px"], + }, + ], + }, + + // Test min-size on multiple items simultaneously: + { + items: [ + { + flex: "auto", + "_min-main-size": "20px", + "_main-size": [null, "20px"], + }, + { + flex: "9 auto", + "_min-main-size": "150px", + "_main-size": ["50px", "180px"], + }, + ], + }, + { + items: [ + { + flex: "1 1 0px", + "_min-main-size": "90px", + "_main-size": [null, "90px"], + }, + { + flex: "1 1 0px", + "_min-main-size": "80px", + "_main-size": [null, "80px"], + }, + { + flex: "1 1 40px", + "_main-size": [null, "30px"], + }, + ], + }, + + // Test a case where _min-main-size will be violated on different items in + // successive iterations of the "resolve the flexible lengths" loop + { + items: [ + { + flex: "1 2 100px", + "_min-main-size": "90px", + "_main-size": [null, "90px"], + }, + { + flex: "1 1 100px", + "_min-main-size": "70px", + "_main-size": [null, "70px"], + }, + { + flex: "1 1 100px", + "_main-size": [null, "40px"], + }, + ], + }, + + // Test some cases that have a min-size violation on one item and a + // max-size violation on another: + + // Here, both items initially grow to 100px. That violates both + // items' sizing constraints (it's smaller than the min-size and larger than + // the max-size), so we clamp both of them and sum the clamping-differences: + // + // (130px - 100px) + (50px - 100px) = (30px) + (-50px) = -20px + // + // This sum is negative, so (per spec) we freeze the item that had its + // max-size violated (the second one) and restart the algorithm. This time, + // all the available space (200px - 50px = 150px) goes to the not-yet-frozen + // first item, and that puts it above its min-size, so all is well. + { + items: [ + { + flex: "auto", + "_min-main-size": "130px", + "_main-size": [null, "150px"], + }, + { + flex: "auto", + "_max-main-size": "50px", + "_main-size": [null, "50px"], + }, + ], + }, + + // As above, both items initially grow to 100px, and that violates both items' + // constraints. However, now the sum of the clamping differences is: + // + // (130px - 100px) + (80px - 100px) = (30px) + (-20px) = 10px + // + // This sum is positive, so (per spec) we freeze the item that had its + // min-size violated (the first one) and restart the algorithm. This time, + // all the available space (200px - 130px = 70px) goes to the not-yet-frozen + // second item, and that puts it below its max-size, so all is well. + { + items: [ + { + flex: "auto", + "_min-main-size": "130px", + "_main-size": [null, "130px"], + }, + { + flex: "auto", + "_max-main-size": "80px", + "_main-size": [null, "70px"], + }, + ], + }, + + // As above, both items initially grow to 100px, and that violates both items' + // constraints. So we clamp both items and sum the clamping differences to + // see what to do next. The sum is: + // + // (80px - 100px) + (120px - 100px) = (-20px) + (20px) = 0px + // + // Per spec, if the sum is 0, we're done -- we leave both items at their + // clamped sizes. + { + items: [ + { + flex: "auto", + "_max-main-size": "80px", + "_main-size": [null, "80px"], + }, + { + flex: "auto", + "_min-main-size": "120px", + "_main-size": [null, "120px"], + }, + ], + }, + + // Test cases where flex-grow sums to less than 1: + // =============================================== + // This makes us treat the flexibilities like "fraction of free space" + // instead of weights, so that e.g. a single item with "flex-grow: 0.1" + // will only get 10% of the free space instead of all of the free space. + + // Basic cases where flex-grow sum is less than 1: + { + items: [ + { + flex: "0.1 100px", + "_main-size": [null, "110px"], // +10% of free space + }, + ], + }, + { + items: [ + { + flex: "0.8 0px", + "_main-size": [null, "160px"], // +80% of free space + }, + ], + }, + + // ... and now with two flex items: + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "110px"], // +40% of free space + }, + { + flex: "0.2 30px", + "_main-size": [null, "50px"], // +20% of free space + }, + ], + }, + + // ...and now with max-size modifying how much free space one item can take: + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "110px"], // +40% of free space + }, + { + flex: "0.2 30px", + "_max-main-size": "35px", + "_main-size": [null, "35px"], // +20% free space, then clamped + }, + ], + }, + // ...and now with a max-size smaller than our flex-basis: + // (This makes us freeze the second item right away, before we compute + // the initial free space.) + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "118px"], // +40% of 200px-70px-10px + }, + { + flex: "0.2 30px", + "_max-main-size": "10px", + "_main-size": [null, "10px"], // immediately frozen + }, + ], + }, + // ...and now with a max-size and a huge flex-basis, such that we initially + // have negative free space, which makes the "% of [original] free space" + // calculations a bit more subtle. We set the "original free space" after + // we've clamped the second item (the first time the free space is positive). + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "118px"], // +40% of free space _after freezing + // the other item_ + }, + { + flex: "0.2 150px", + "_max-main-size": "10px", + "_main-size": [null, "10px"], // clamped immediately + }, + ], + }, + + // Now with min-size modifying how much free space our items take: + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "110px"], // +40% of free space + }, + { + flex: "0.2 30px", + "_min-main-size": "70px", + "_main-size": [null, "70px"], // +20% free space, then clamped + }, + ], + }, + + // ...and now with a large enough min-size that it prevents the other flex + // item from taking its full desired portion of the original free space: + { + items: [ + { + flex: "0.4 70px", + "_main-size": [null, "80px"], // (Can't take my full +40% of + // free space due to other item's + // large min-size.) + }, + { + flex: "0.2 30px", + "_min-main-size": "120px", + "_main-size": [null, "120px"], // +20% free space, then clamped + }, + ], + }, + // ...and now with a large-enough min-size that it pushes the other flex item + // to actually shrink a bit (with default "flex-shrink:1"): + { + items: [ + { + flex: "0.3 30px", + "_main-size": [null, "20px"], // -10px, instead of desired +45px + }, + { + flex: "0.2 20px", + "_min-main-size": "180px", + "_main-size": [null, "180px"], // +160px, instead of desired +30px + }, + ], + }, + + // In this case, the items' flexibilities don't initially sum to < 1, but they + // do after we freeze the third item for violating its max-size. + { + items: [ + { + flex: "0.3 30px", + "_main-size": [null, "75px"], + // 1st loop: desires (0.3 / 5) * 150px = 9px. Tentatively granted. + // 2nd loop: desires 0.3 * 150px = 45px. Tentatively granted. + // 3rd loop: desires 0.3 * 150px = 45px. Granted +45px. + }, + { + flex: "0.2 20px", + "_max-main-size": "30px", + "_main-size": [null, "30px"], + // First loop: desires (0.2 / 5) * 150px = 6px. Tentatively granted. + // Second loop: desires 0.2 * 150px = 30px. Frozen at +10px. + }, + { + flex: "4.5 0px", + "_max-main-size": "20px", + "_main-size": [null, "20px"], + // First loop: desires (4.5 / 5) * 150px = 135px. Frozen at +20px. + }, + ], + }, + + // Make sure we calculate "original free space" correctly when one of our + // flex items will be clamped right away, due to max-size preventing it from + // growing at all: + { + // Here, the second flex item is effectively inflexible; it's + // immediately frozen at 40px since we're growing & this item's max size + // trivially prevents it from growing. This leaves us with an "original + // free space" of 60px. The first flex item takes half of that, due to + // its flex-grow value of 0.5. + items: [ + { + flex: "0.5 100px", + "_main-size": [null, "130px"], + }, + { + flex: "1 98px", + "_max-main-size": "40px", + "_main-size": [null, "40px"], + }, + ], + }, + { + // Same as previous example, but with a larger flex-basis on the second + // element (which shouldn't ultimately matter, because its max size clamps + // its size immediately anyway). + items: [ + { + flex: "0.5 100px", + "_main-size": [null, "130px"], + }, + { + flex: "1 101px", + "_max-main-size": "40px", + "_main-size": [null, "40px"], + }, + ], + }, + + { + // Here, the third flex item is effectively inflexible; it's immediately + // frozen at 0px since we're growing & this item's max size trivially + // prevents it from growing. This leaves us with an "original free space" of + // 100px. The first flex item takes 40px, and the third takes 50px, due to + // their flex values of 0.4 and 0.5. + items: [ + { + flex: "0.4 50px", + "_main-size": [null, "90px"], + }, + { + flex: "0.5 50px", + "_main-size": [null, "100px"], + }, + { + flex: "0 90px", + "_max-main-size": "0px", + "_main-size": [null, "0px"], + }, + ], + }, + { + // Same as previous example, but with slightly larger flex-grow values on + // the first and second items, which sum to 1.0 and produce slightly larger + // main sizes. This demonstrates that there's no discontinuity between the + // "< 1.0 sum" to ">= 1.0 sum" behavior, in this situation at least. + items: [ + { + flex: "0.45 50px", + "_main-size": [null, "95px"], + }, + { + flex: "0.55 50px", + "_main-size": [null, "105px"], + }, + { + flex: "0 90px", + "_max-main-size": "0px", + "_main-size": [null, "0px"], + }, + ], + }, + + // Test cases where flex-shrink sums to less than 1: + // ================================================= + // This makes us treat the flexibilities more like "fraction of (negative) + // free space" instead of weights, so that e.g. a single item with + // "flex-shrink: 0.1" will only shrink by 10% of amount that it overflows + // its container by. + // + // It gets a bit more complex when there are multiple flex items, because + // flex-shrink is scaled by the flex-basis before it's used as a weight. But + // even with that scaling, the general principal is that e.g. if the + // flex-shrink values *sum* to 0.6, then the items will collectively only + // shrink by 60% (and hence will still overflow). + + // Basic cases where flex-grow sum is less than 1: + { + items: [ + { + flex: "0 0.1 300px", + "_main-size": [null, "290px"], // +10% of (negative) free space + }, + ], + }, + { + items: [ + { + flex: "0 0.8 400px", + "_main-size": [null, "240px"], // +80% of (negative) free space + }, + ], + }, + + // ...now with two flex items, with the same flex-basis value: + { + items: [ + { + flex: "0 0.4 150px", + "_main-size": [null, "110px"], // +40% of (negative) free space + }, + { + flex: "0 0.2 150px", + "_main-size": [null, "130px"], // +20% of (negative) free space + }, + ], + }, + + // ...now with two flex items, with different flex-basis values (and hence + // differently-scaled flex factors): + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "76px"], + }, + { + flex: "0 0.1 200px", + "_main-size": [null, "184px"], + }, + ], + // Notes: + // - Free space: -100px + // - Sum of flex-shrink factors: 0.3 + 0.1 = 0.4 + // - Since that sum ^ is < 1, we'll only distribute that fraction of + // the free space. We'll distribute: -100px * 0.4 = -40px + // + // - 1st item's scaled flex factor: 0.3 * 100px = 30 + // - 2nd item's scaled flex factor: 0.1 * 200px = 20 + // - 1st item's share of distributed free space: 30/(30+20) = 60% + // - 2nd item's share of distributed free space: 20/(30+20) = 40% + // + // SO: + // - 1st item gets 60% * -40px = -24px. 100px-24px = 76px + // - 2nd item gets 40% * -40px = -16px. 200px-16px = 184px + }, + + // ...now with min-size modifying how much one item can shrink: + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "70px"], + }, + { + flex: "0 0.1 200px", + "_min-main-size": "190px", + "_main-size": [null, "190px"], + }, + ], + // Notes: + // - We proceed as in previous testcase, but clamp the second flex item + // at its min main size. + // - After that point, we have a total flex-shrink of = 0.3, so we + // distribute 0.3 * -100px = -30px to the remaining unfrozen flex + // items. Since there's only one unfrozen item left, it gets all of it. + }, + + // ...now with min-size larger than our flex-basis: + // (This makes us freeze the second item right away, before we compute + // the initial free space.) + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "55px"], // +30% of 200px-100px-250px + }, + { + flex: "0 0.1 200px", + "_min-main-size": "250px", + "_main-size": [null, "250px"], // immediately frozen + }, + ], + // (Same as previous example, except the min-main-size prevents the + // second item from shrinking at all) + }, + + // ...and now with a min-size and a small flex-basis, such that we initially + // have positive free space, which makes the "% of [original] free space" + // calculations a bit more subtle. We set the "original free space" after + // we've clamped the second item (the first time the free space is negative). + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "70px"], + }, + { + flex: "0 0.1 50px", + "_min-main-size": "200px", + "_main-size": [null, "200px"], + }, + ], + }, + + // Now with max-size making an item shrink more than its flex-shrink value + // calls for: + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "70px"], + }, + { + flex: "0 0.1 200px", + "_max-main-size": "150px", + "_main-size": [null, "150px"], + }, + ], + // Notes: + // - We proceed as in an earlier testcase, but clamp the second flex item + // at its max main size. + // - After that point, we have a total flex-shrink of = 0.3, so we + // distribute 0.3 * -100px = -30px to the remaining unfrozen flex + // items. Since there's only one unfrozen item left, it gets all of it. + }, + + // ...and now with a small enough max-size that it prevents the other flex + // item from taking its full desired portion of the (negative) original free + // space: + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "90px"], + }, + { + flex: "0 0.1 200px", + "_max-main-size": "110px", + "_main-size": [null, "110px"], + }, + ], + // Notes: + // - We proceed as in an earlier testcase, but clamp the second flex item + // at its max main size. + // - After that point, we have a total flex-shrink of 0.3, which would + // have us distribute 0.3 * -100px = -30px to the (one) remaining + // unfrozen flex item. But our remaining free space is only -10px at + // that point, so we distribute that instead. + }, + + // ...and now with a small enough max-size that it pushes the other flex item + // to actually grow a bit (with custom "flex-grow: 1" for this testcase): + { + items: [ + { + flex: "1 0.3 100px", + "_main-size": [null, "120px"], + }, + { + flex: "1 0.1 200px", + "_max-main-size": "80px", + "_main-size": [null, "80px"], + }, + ], + }, + + // In this case, the items' flexibilities don't initially sum to < 1, but they + // do after we freeze the third item for violating its min-size. + { + items: [ + { + flex: "0 0.3 100px", + "_main-size": [null, "76px"], + }, + { + flex: "0 0.1 150px", + "_main-size": [null, "138px"], + }, + { + flex: "0 0.8 10px", + "_min-main-size": "40px", + "_main-size": [null, "40px"], + }, + ], + // Notes: + // - We immediately freeze the 3rd item, since we're shrinking and its + // min size obviously prevents it from shrinking at all. This leaves + // 200px - 100px - 150px - 40px = -90px of "initial free space". + // + // - Our remaining flexible items have a total flex-shrink of 0.4, + // so we can distribute a total of 0.4 * -90px = -36px + // + // - We distribute that space using *scaled* flex factors: + // * 1st item's scaled flex factor: 0.3 * 100px = 30 + // * 2nd item's scaled flex factor: 0.1 * 150px = 15 + // ...which means... + // * 1st item's share of distributed free space: 30/(30+15) = 2/3 + // * 2nd item's share of distributed free space: 15/(30+15) = 1/3 + // + // SO: + // - 1st item gets 2/3 * -36px = -24px. 100px - 24px = 76px + // - 2nd item gets 1/3 * -36px = -12px. 150px - 12px = 138px + }, + + // In this case, the items' flexibilities sum to > 1, in part due to an item + // that *can't actually shrink* due to its 0 flex-basis (which gives it a + // "scaled flex factor" of 0). This prevents us from triggering the special + // behavior for flexibilities that sum to less than 1, and as a result, the + // first item ends up absorbing all of the free space. + { + items: [ + { + flex: "0 .5 300px", + "_main-size": [null, "200px"], + }, + { + flex: "0 5 0px", + "_main-size": [null, "0px"], + }, + ], + }, + + // This case is similar to the one above, but with a *barely* nonzero base + // size for the second item. This should produce a result similar to the case + // above. (In particular, we should first distribute a very small amount of + // negative free space to the second item, getting it to approximately zero, + // and distribute the bulk of the negative free space to the first item, + // getting it to approximately 200px.) + { + items: [ + { + flex: "0 .5 300px", + "_main-size": [null, "200px"], + }, + { + flex: "0 1 0.01px", + "_main-size": [null, "0px"], + }, + ], + }, + // This case is similar to the ones above, but now we've increased the + // flex-shrink value on the second-item so that it claims enough of the + // negative free space to go below its min-size (0px). So, it triggers a min + // violation & is frozen. For the loop *after* the min violation, the sum of + // the remaining flex items' flex-shrink values is less than 1, so we trigger + // the special <1 behavior and only distribute half of the remaining + // (negative) free space to the first item (instead of all of it). + { + items: [ + { + flex: "0 .5 300px", + "_main-size": [null, "250px"], + }, + { + flex: "0 5 0.01px", + "_main-size": [null, "0px"], + }, + ], + }, +]; |