Skip to content

Commit

Permalink
Merge pull request #190 from Confidenceman02/virtual-scroll-multi-var…
Browse files Browse the repository at this point in the history
…iant

Virtual scroll multi variant
  • Loading branch information
Confidenceman02 authored Aug 3, 2024
2 parents 6b5c1ae + 20b1886 commit 9ed8d93
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 38 deletions.
2 changes: 2 additions & 0 deletions docs/index.18782437.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/index.18782437.css.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions docs/index.5d2c5749.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/index.5d2c5749.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Geora components</title><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Elm select"><meta name="author" content="Geora"><link rel="stylesheet" href="index.91cabaa0.css"></head><body> <main></main> <script type="module" src="index.75a13ff1.js"></script> </body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Geora components</title><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Elm select"><meta name="author" content="Geora"><link rel="stylesheet" href="index.18782437.css"></head><body> <main></main> <script type="module" src="index.5d2c5749.js"></script> </body></html>
60 changes: 31 additions & 29 deletions elm-book/elm.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
{
"type": "application",
"source-directories": ["src"],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"Confidenceman02/elm-select": "10.4.3",
"dtwrks/elm-book": "1.4.2",
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"rtfeldman/elm-css": "18.0.0"
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"Confidenceman02/elm-select": "11.0.0",
"dtwrks/elm-book": "1.4.2",
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"rtfeldman/elm-css": "18.0.0"
},
"indirect": {
"dillonkearns/elm-markdown": "7.0.1",
"elm/json": "1.1.3",
"elm/parser": "1.1.0",
"elm/regex": "1.0.0",
"elm/svg": "1.0.1",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3",
"elm-community/list-extra": "8.7.0",
"pablohirafuji/elm-syntax-highlight": "3.5.0",
"robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-hex": "1.0.0"
}
},
"indirect": {
"dillonkearns/elm-markdown": "7.0.1",
"elm/json": "1.1.3",
"elm/parser": "1.1.0",
"elm/regex": "1.0.0",
"elm/svg": "1.0.1",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3",
"elm-community/list-extra": "8.7.0",
"pablohirafuji/elm-syntax-highlight": "3.5.0",
"robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-hex": "1.0.0"
"test-dependencies": {
"direct": {},
"indirect": {}
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}
2 changes: 1 addition & 1 deletion examples-optimized/elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"elm/html": "1.0.0",
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm-community/list-extra": "8.3.0",
"elm-community/list-extra": "8.7.0",
"rtfeldman/elm-css": "18.0.0"
},
"indirect": {
Expand Down
94 changes: 94 additions & 0 deletions examples/MultiVirtual.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
module MultiVirtual exposing (..)

import Browser
import Css
import Html.Styled as Styled exposing (Html, div)
import Html.Styled.Attributes as StyledAttribs
import Select exposing (MenuItem, initState, selectIdentifier, update)


type Msg
= SelectMsg (Select.Msg Int)


type alias Model =
{ selectState : Select.State
, items : List (MenuItem Int)
, selectedItems : List Int
}


init : ( Model, Cmd Msg )
init =
( { selectState = initState (selectIdentifier "SingleSelectExample")
, items =
List.range 0 1000
|> List.map
(\i ->
Select.basicMenuItem { item = i, label = String.fromInt i }
)
, selectedItems = []
}
, Cmd.none
)


main : Program () Model Msg
main =
Browser.element
{ init = always init
, view = view >> Styled.toUnstyled
, update = update
, subscriptions = \_ -> Sub.none
}


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SelectMsg sm ->
let
( maybeAction, selectState, cmds ) =
Select.update sm model.selectState

updatedSelectedItems =
case maybeAction of
Just (Select.Select i) ->
model.selectedItems ++ [ i ]

Just (Select.Deselect deletedItems) ->
List.filter (\i -> not (List.member i deletedItems)) model.selectedItems

Just Select.Clear ->
[]

_ ->
model.selectedItems
in
( { model | selectState = selectState, selectedItems = updatedSelectedItems }, Cmd.map SelectMsg cmds )


view : Model -> Html Msg
view m =
let
toMenuItem item =
Select.basicMenuItem { item = item, label = String.fromInt item }
in
div
[ StyledAttribs.css
[ Css.marginTop (Css.px 20)
, Css.width (Css.pct 50)
, Css.marginLeft Css.auto
, Css.marginRight Css.auto
]
]
[ Styled.map SelectMsg <|
Select.viewVirtual
(Select.multiVirtual (List.map toMenuItem m.selectedItems)
|> Select.state m.selectState
|> Select.menuItemsVirtual (Select.virtualFixedMenuItems 35 m.items)
|> Select.placeholder "Placeholder"
|> Select.searchable True
|> Select.clearable True
)
]
70 changes: 68 additions & 2 deletions src/Select.elm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Select exposing
, Action(..), initState, keepMenuOpen, focus, isFocused, isMenuOpen, Msg
, menuItems, menuItemsVirtual, clearable
, placeholder, selectIdentifier, staticSelectIdentifier, state, update, view, viewVirtual, searchable, setStyles, name
, single, singleVirtual
, single, singleVirtual, multiVirtual
, singleMenu, menu
, multi
, singleNative
Expand All @@ -25,7 +25,7 @@ module Select exposing
# Single select
@docs single, singleVirtual
@docs single, singleVirtual, multiVirtual
# Menu select
Expand Down Expand Up @@ -1379,6 +1379,30 @@ singleVirtual maybeSelectedItem =
Config { defaultsVirtualVariant | variant = CustomVariant (SingleVirtual maybeSelectedItem) }


{-| Select a multi virtual item.
countries : List (MenuItem Country)
countries =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuitem
{ item = Taiwan, label = "Taiwan"
-- other countries
]
yourView =
Html.map SelectMsg <|
viewVirtual
(multiVirtual [] |>
menuItemsVirtual (virtualFixedMenuItems 35 countries)
)
-}
multiVirtual : MenuItems item -> Config item (VirtualConfig item)
multiVirtual selectedItems =
Config { defaultsVirtualVariant | variant = CustomVariant (Multi selectedItems) }


{-| Menu only single select.
countries : List (MenuItem Country)
Expand Down Expand Up @@ -2353,6 +2377,48 @@ viewVirtual (Config config) =
++ viewHiddenFormControl variant config.name
)

CustomVariant ((Multi _) as variant) ->
viewWrapper
(ViewWrapperData viewData.state
config.searchable
variant
config.disabled
)
([ lazy viewCustomControl
(ViewCustomControlData
config.state
viewData.ctrlStyles
config.styles
(enterSelectTargetItem viewData.state virtualizedMenuItemsUnwrapped)
filteredCount
variant
config.placeholder
config.disabled
config.searchable
config.labelledBy
config.ariaDescribedBy
config.clearable
config.isLoading
)
, lazy renderMenu
(RenderMenuData viewData.state.menuOpen
viewData.state.initialAction
viewData.state.activeTargetIndex
viewData.state.menuNavigation
viewData.state.controlUiFocused
config.isLoading
(MenuItemsVirtual_ virtualizedMenuItems)
variant
config.loadingMessage
config.styles
viewData.selectId
config.disabled
filteredCount
)
]
++ viewHiddenFormControl variant config.name
)

_ ->
text ""

Expand Down
83 changes: 78 additions & 5 deletions tests/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe("examples", () => {
"text=SingleMenuOpen.elm",
);
const singleVirtualVisible = await page.isVisible("text=SingleVirtual.elm");
const multiVirtualVisible = await page.isVisible("text=MultiVirtual.elm");

expect(singleSearchableVisible).toBeTruthy();
expect(nativeSingle).toBeTruthy();
Expand All @@ -55,7 +56,81 @@ describe("examples", () => {
});
});

describe.only("SingleVirtual", () => {
describe("MultiVirtual", () => {
// has 1000 menu items
it("Only renders the first 8 items", async () => {
await browser.newContext();
const page = await browser.newPage();

await page.goto(`${BASE_URI}/MultiVirtual.elm`);
await page.click("[data-test-id=selectContainer]");
await page.waitForSelector("[data-test-id=listBox]");
const options = page.getByRole("option");

expect(options).toHaveCount(8);
});

it("Jumps to the end of the list", async () => {
await browser.newContext();
const page = await browser.newPage();

await page.goto(`${BASE_URI}/MultiVirtual.elm`);
await page.click("[data-test-id=selectContainer]");
await page.waitForSelector("[data-test-id=listBox]");
await page.keyboard.press("ArrowUp");
const lastItem = page.getByRole("option", { name: "1000" });

expect(lastItem).toBeVisible();
});

it("Jumps to start of the list", async () => {
await browser.newContext();
const page = await browser.newPage();

await page.goto(`${BASE_URI}/MultiVirtual.elm`);
await page.click("[data-test-id=selectContainer]");
await page.waitForSelector("[data-test-id=listBox]");
await page.keyboard.press("ArrowUp");
const lastItem = page.getByRole("option", { name: "1000" });

expect(lastItem).toBeVisible();

await page.keyboard.press("ArrowDown");
const firstItem = page.getByRole("option", { name: "0" });

expect(firstItem).toBeVisible();
});

it("loads more options on scroll", async () => {
await browser.newContext();
const page = await browser.newPage();

await page.goto(`${BASE_URI}/MultiVirtual.elm`);
await page.click("[data-test-id=selectContainer]");
await page.waitForSelector("[data-test-id=listBox]");
const listbox = page.locator("[data-test-id=listBox]");
const [x, y, width, height] = await listbox.evaluate((ele: HTMLElement) => {
const boundingRect = ele.getBoundingClientRect();
return [
boundingRect.x,
boundingRect.y,
ele.clientWidth,
ele.clientHeight,
];
});

// Position mouse in the middle of listbox
await page.mouse.move((x + width) / 2, (y + height) / 2);
await page.mouse.wheel(0, 230);
await page.waitForTimeout(100);

const item = page.getByRole("option", { name: "11" });

expect(item).toBeVisible();
});
});

describe("SingleVirtual", () => {
// has 1000 menu items
it("Only renders the first 8 items", async () => {
await browser.newContext();
Expand Down Expand Up @@ -104,10 +179,8 @@ describe.only("SingleVirtual", () => {
await browser.newContext();
const page = await browser.newPage();

await page.goto(`${BASE_URI}/SingleMenu.elm`);
await page.click("[data-test-id=dropdown]");
await page.waitForSelector("[data-test-id=selectInput]");
await page.locator("[data-test-id=selectContainer]").click();
await page.goto(`${BASE_URI}/SingleVirtual.elm`);
await page.click("[data-test-id=selectContainer]");
await page.waitForSelector("[data-test-id=listBox]");
const listbox = page.locator("[data-test-id=listBox]");
const [x, y, width, height] = await listbox.evaluate((ele: HTMLElement) => {
Expand Down

0 comments on commit 9ed8d93

Please sign in to comment.