How to use stateRef method in storybook-root

Best JavaScript code snippet using storybook-root

StudyDetail.js

Source:StudyDetail.js Github

copy

Full Screen

1import React, { useEffect, useState, useRef, useContext } from "react";2import { useHistory } from "react-router-dom";3import { FullScreen, useFullScreenHandle } from "react-full-screen";4import { Modal, Tooltip, Grid, Typography, Box } from "@material-ui/core";5import IconButton from "@material-ui/core/IconButton";6import MenuIcon from "@material-ui/icons/Menu";7import PlayArrowIcon from "@material-ui/icons/PlayArrow";8import PauseIcon from "@material-ui/icons/Pause";9import FastForwardIcon from "@material-ui/icons/FastForward";10import FastRewindIcon from "@material-ui/icons/FastRewind";11import FullscreenIcon from "@material-ui/icons/Fullscreen";12import RedoIcon from "@material-ui/icons/Redo";13import UndoIcon from "@material-ui/icons/Undo";14import Save from "@material-ui/icons/Save";15import AssessmentIcon from "@material-ui/icons/Assessment";16import { MessagedProgress } from "../MessagedProgress";17import Plots from "./Plots";18import ChannelDataList from "../../models/ChannelDataList";19import TimeRange from "../../models/TimeRange";20import { XAxis } from "./XAxis";21import { TimeNavigation } from "./TimeNavigation";22import ServerClient from "../../models/ServerClient";23import PropertyMenu from "./PropertyMenu";24import StorageUtility, { PlotPropertyName } from "../../models/StorageUtility";25import { debounce } from "../../models/Utilities";26import PSGScoring from "../../models/PSGScoring";27import { DataGrid, Watermark } from "@material-ui/data-grid";28import PSGScoreReport from "./PSGScoreReport";29import { UserContext } from "../UserContext";30import { Opacity } from "@material-ui/icons";31const epochDuration = 30;32const columns = [33 {34 field: "id",35 headerName: "ID",36 flex: 0.05,37 type: "string",38 hide: true,39 filterable: false,40 sortable: false,41 align: "center",42 headerAlign: "center",43 },44 {45 field: "time",46 headerName: "Time",47 width: 90,48 type: "string",49 hide: false,50 filterable: false,51 sortable: false,52 align: "center",53 headerAlign: "center",54 renderCell: (params) => (55 <Tooltip title={new Date(params.value * 1000).toLocaleString()}>56 <span>{new Date(params.value * 1000).toLocaleTimeString()}</span>57 </Tooltip>58 ),59 },60 {61 field: "comment",62 headerName: "Annotation",63 flex: 0.5,64 type: "string",65 hide: false,66 filterable: false,67 sortable: false,68 align: "left",69 headerAlign: "center",70 renderCell: (params) => (71 <Tooltip title={params.row.channel + ": " + params.value}>72 <span>{params.value}</span>73 </Tooltip>74 ),75 },76];77export function StudyDetail() {78 const history = useHistory();79 const { sessionInfo, setSessionInfo } = useContext(UserContext);80 const studyInfo = history.location.state;81 const [state, setState] = useState({82 dataTimeRange: new TimeRange([83 studyInfo.startTime,84 studyInfo.endTime ? studyInfo.endTime : Date.now() / 1000,85 ]),86 displayTimeRange: new TimeRange([87 studyInfo.startTime,88 studyInfo.startTime +89 StorageUtility.getPlotProperty(PlotPropertyName.displayInterval),90 ]),91 displayInterval: StorageUtility.getPlotProperty(92 PlotPropertyName.displayInterval93 ),94 sliderValue: 0,95 resizeCount: 0,96 dataList: studyInfo.dataList,97 errorMessage: "",98 refreshPlots: true,99 hasFetchedAllData: studyInfo.dataList ? true : false,100 isLiveData: studyInfo.isLiveData,101 openMenu: false,102 openReport: false,103 enablePSGScoring: false,104 playingSpeed:105 -1 * StorageUtility.getPlotProperty(PlotPropertyName.playingSpeed),106 });107 const stateRef = useRef({});108 stateRef.current = state;109 const handleMenuClick = () => {110 setState({111 ...stateRef.current,112 openMenu: true,113 refreshPlots: false,114 });115 };116 const handleMenuClose = () => {117 setState({118 ...stateRef.current,119 openMenu: false,120 refreshPlots: false,121 });122 };123 const handleIntervalChange = (event) => {124 let newValue = event.target.value;125 StorageUtility.updatePlotProperty(126 PlotPropertyName.displayInterval,127 newValue128 );129 updateDisplayInterval(newValue);130 };131 const handleSliderChange = (event, newValue) => {132 if (event !== null) {133 event.stopPropagation();134 }135 console.log("Handling slider change: " + newValue);136 if (newValue === stateRef.current.sliderValue) {137 return;138 } else if (newValue >= stateRef.current.dataTimeRange.duration) {139 setState({140 ...stateRef.current,141 refreshPlots: true,142 });143 return;144 } else if (145 stateRef.current.dataList &&146 stateRef.current.dataList.timeRange.duration >147 stateRef.current.displayInterval148 ) {149 let sliderValue = Math.min(150 Math.max(151 newValue,152 stateRef.current.dataList.timeRange.startTime -153 stateRef.current.dataTimeRange.startTime154 ),155 stateRef.current.dataList.timeRange.endTime -156 stateRef.current.dataTimeRange.startTime -157 stateRef.current.displayInterval * 0158 );159 let displayTimeRange = new TimeRange([160 stateRef.current.dataTimeRange.startTime + sliderValue,161 stateRef.current.dataTimeRange.startTime +162 sliderValue +163 stateRef.current.displayInterval,164 ]);165 setState({166 ...stateRef.current,167 sliderValue: sliderValue,168 displayTimeRange: displayTimeRange,169 refreshPlots: true,170 });171 if (sliderValue < newValue && stateRef.current.playingSpeed > 0) {172 handlePlayPause();173 }174 }175 };176 function play() {177 if (stateRef.current.playingSpeed > 0) {178 let timeout = 10;179 if (stateRef.current.playingSpeed < 0.4) {180 timeout = 2000;181 } else if (stateRef.current.playingSpeed < 2.5) {182 timeout = 1000;183 } else if (stateRef.current.playingSpeed < 5) {184 timeout = 500;185 }186 let moveIntervalFraction = 1;187 if (stateRef.current.playingSpeed < 0.2) {188 moveIntervalFraction = 1 / 30;189 } else if (stateRef.current.playingSpeed < 0.4) {190 moveIntervalFraction = 1 / 15;191 } else if (stateRef.current.playingSpeed < 0.6) {192 moveIntervalFraction = 1 / 6;193 } else if (stateRef.current.playingSpeed < 1.2) {194 moveIntervalFraction = 1 / 3;195 }196 Math.min(2000, 1000 / stateRef.current.playingSpeed);197 const moveInterval =198 moveIntervalFraction * stateRef.current.displayInterval;199 handleSliderChange(null, stateRef.current.sliderValue + moveInterval);200 setTimeout(() => {201 play(timeout);202 }, timeout);203 }204 }205 const handleFastRewind = () => {206 if (stateRef.current.playingSpeed > 0) {207 StorageUtility.updatePlotProperty(208 PlotPropertyName.playingSpeed,209 stateRef.current.playingSpeed / 2210 );211 setState({212 ...stateRef.current,213 playingSpeed: stateRef.current.playingSpeed / 2,214 });215 } else {216 handleSliderChange(217 null,218 stateRef.current.sliderValue - 2 * stateRef.current.displayInterval219 );220 }221 };222 const handlePlayPause = () => {223 setState({224 ...stateRef.current,225 playingSpeed: -stateRef.current.playingSpeed,226 });227 setTimeout(() => {228 play();229 }, 500);230 };231 const handleFastForward = () => {232 if (stateRef.current.playingSpeed > 0) {233 StorageUtility.updatePlotProperty(234 PlotPropertyName.playingSpeed,235 stateRef.current.playingSpeed * 2236 );237 setState({238 ...stateRef.current,239 playingSpeed: stateRef.current.playingSpeed * 2,240 });241 } else {242 handleSliderChange(243 null,244 stateRef.current.sliderValue + 2 * stateRef.current.displayInterval245 );246 }247 };248 const handleChannelCheckboxChange = (channelName) => (event) => {249 StorageUtility.updateChannelProperty(250 channelName,251 "visible",252 event.target.checked253 );254 setState({255 ...stateRef.current,256 refreshPlots: true,257 });258 };259 const handleBackgroundColorChange = (newColor) => {260 StorageUtility.updatePlotProperty(261 PlotPropertyName.backgroundColor,262 newColor263 );264 setState({265 ...stateRef.current,266 refreshPlots: true,267 });268 };269 const handleShowGridChange = (event) => {270 StorageUtility.updatePlotProperty(271 PlotPropertyName.showGrid,272 event.target.checked273 );274 setState({275 ...stateRef.current,276 refreshPlots: true,277 });278 };279 const handleShowChannelScaleChange = (event) => {280 StorageUtility.updatePlotProperty(281 PlotPropertyName.showChannelScale,282 event.target.checked283 );284 setState({285 ...stateRef.current,286 refreshPlots: true,287 });288 };289 const handleShowCommentWindowChange = (event) => {290 StorageUtility.updatePlotProperty(291 PlotPropertyName.showCommentWindow,292 event.target.checked293 );294 setState({295 ...stateRef.current,296 refreshPlots: true,297 });298 };299 const handleShowChannelLabelChange = (event) => {300 StorageUtility.updatePlotProperty(301 PlotPropertyName.showChannelLabel,302 event.target.checked303 );304 setState({305 ...stateRef.current,306 refreshPlots: true,307 });308 };309 const handlePreferenceChange = () => {310 const displayInterval = StorageUtility.getPlotProperty(311 PlotPropertyName.displayInterval312 );313 updateDisplayInterval(displayInterval);314 };315 const handleWatermarkChange = (watermark) => (event) => {316 let currentWatermark = StorageUtility.getPlotProperty(317 PlotPropertyName.watermark318 );319 if (event.target.checked) {320 currentWatermark += watermark;321 } else {322 currentWatermark = currentWatermark.replace(watermark, "");323 }324 StorageUtility.updatePlotProperty(325 PlotPropertyName.watermark,326 currentWatermark327 );328 setState({329 ...stateRef.current,330 refreshPlots: true,331 });332 };333 const handlePSGScoringChange = (userID) => (event) => {334 if (event.target.checked) {335 stateRef.current.dataList.initializeNewScore(336 userID,337 studyInfo.dataIDs[0],338 new TimeRange([studyInfo.startTime, studyInfo.endTime])339 );340 }341 setState({342 ...stateRef.current,343 enablePSGScoring: event.target.checked,344 refreshPlots: true,345 });346 };347 const handleRespiratoryChannelChange = (event) => {348 StorageUtility.updatePlotProperty(349 PlotPropertyName.respiratoryChannel,350 event.target.value351 );352 setState({353 ...stateRef.current,354 refreshPlots: true,355 });356 };357 const handleChannelLabelFontSizeChange = (event) => {358 StorageUtility.updatePlotProperty(359 PlotPropertyName.channelLabelFontSize,360 event.target.value361 );362 setState({363 ...stateRef.current,364 refreshPlots: true,365 });366 };367 const handleCommentWindowDoubleClick = (event) => {368 console.log(event);369 let sliderValue =370 stateRef.current.displayInterval *371 Math.floor(372 (event.row.time - stateRef.current.dataTimeRange.startTime) /373 stateRef.current.displayInterval374 );375 handleSliderChange(null, sliderValue);376 };377 const handleRefreshPlots = (channelName) => {378 setState({379 ...stateRef.current,380 refreshPlots: true,381 });382 };383 const handleEpochClick = (deltaEpoch) => {384 const moveStep = 30;385 if (deltaEpoch !== 0) {386 handleSliderChange(387 null,388 Math.floor(stateRef.current.sliderValue / moveStep) * moveStep +389 deltaEpoch * moveStep390 );391 }392 };393 const handleKeydown = (e) => {394 e.stopPropagation();395 let moveStep = 30;396 if (stateRef.current.enablePSGScoring) {397 const epochNumber =398 Math.ceil(stateRef.current.sliderValue / epochDuration) + 1;399 switch (e.key) {400 case "0":401 case "w":402 case "W":403 stateRef.current.dataList.PSGScores.updateStage(epochNumber, 0);404 break;405 case "1":406 stateRef.current.dataList.PSGScores.updateStage(epochNumber, 2);407 break;408 case "2":409 stateRef.current.dataList.PSGScores.updateStage(epochNumber, 3);410 break;411 case "3":412 case "4":413 stateRef.current.dataList.PSGScores.updateStage(epochNumber, 4);414 break;415 case "r":416 case "R":417 stateRef.current.dataList.PSGScores.updateStage(epochNumber, 1);418 break;419 default:420 moveStep = 0;421 break;422 }423 } else {424 moveStep = 0;425 }426 if (!e.path || !e.path[0].className.includes("MuiSlider")) {427 switch (e.key) {428 case "ArrowRight":429 if (e.shiftKey || e.ctrlKey) {430 moveStep = stateRef.current.displayInterval / 2;431 } else {432 moveStep = stateRef.current.displayInterval;433 }434 break;435 case "ArrowLeft":436 if (e.shiftKey || e.ctrlKey) {437 moveStep = -stateRef.current.displayInterval / 2;438 } else {439 moveStep = -stateRef.current.displayInterval;440 }441 break;442 default:443 break;444 }445 }446 if (moveStep !== 0) {447 handleSliderChange(448 null,449 Math.floor(stateRef.current.sliderValue / moveStep) * moveStep +450 moveStep451 );452 }453 };454 const handleSaveScoring = (event) => {455 state.dataList.PSGScores.isDirty = false;456 ServerClient.savePSGScores(state.dataList.PSGScores.getDataChunks());457 };458 const handleClearAll = (event) => {459 state.dataList.PSGScores.clearAllEvents();460 moveToEpoch(1);461 };462 const handleViewScoringReport = (event) => {463 console.log(event);464 setState({465 ...stateRef.current,466 openReport: true,467 });468 };469 const handleReportClose = (event) => {470 setState({471 ...stateRef.current,472 openReport: false,473 });474 };475 const handleUndoScoring = (event) => {476 const epoch = state.dataList.PSGScores.undo();477 moveToEpoch(epoch);478 };479 const handleRedoScoring = (event) => {480 const epoch = state.dataList.PSGScores.redo();481 moveToEpoch(epoch);482 };483 function moveToEpoch(epoch) {484 const newSliderValue = (epoch - 1) * epochDuration;485 if (epoch && newSliderValue !== stateRef.current.sliderValue) {486 handleSliderChange(null, newSliderValue);487 } else {488 setState({489 ...stateRef.current,490 refreshPlots: true,491 });492 }493 }494 useEffect(() => {495 const debounceResizeHandler = debounce(() => {496 setState({497 ...stateRef.current,498 resizeCount: state.resizeCount + 1,499 refreshPlots: true,500 });501 }, 250);502 document.addEventListener("keyup", handleKeydown);503 window.addEventListener("resize", debounceResizeHandler);504 fetchData(true);505 return () => {506 document.removeEventListener("keyup", handleKeydown);507 window.removeEventListener("resize", debounceResizeHandler);508 };509 }, []);510 const fullScreenHandle = useFullScreenHandle();511 if (state.dataList) {512 let displayDataList = {};513 if (state.displayTimeRange.duration === 0) {514 updateDisplayInterval(state.displayInterval);515 }516 if (state.refreshPlots) {517 console.log("getData called.");518 displayDataList = state.dataList.getData(519 state.displayTimeRange,520 window.innerWidth521 );522 }523 let progressBarValue =524 (100 * state.dataList.timeRange.duration) / state.dataTimeRange.duration;525 if (state.dataList.timeRange.startTime > state.dataTimeRange.startTime) {526 progressBarValue = 200 - progressBarValue;527 } else if (state.isLiveData && progressBarValue > 98) {528 progressBarValue = 98;529 }530 const sliderMax = state.dataTimeRange.duration - state.displayInterval;531 const plotProperties = StorageUtility.getPlotProperties();532 const timeNavigationWidth = window.innerWidth - 260;533 const buttonIconMarginTop = state.enablePSGScoring ? -15 : -25;534 const epochNumber =535 Math.ceil(stateRef.current.sliderValue / epochDuration) + 1;536 const comments = state.dataList.comments;537 let width = window.innerWidth;538 if (plotProperties.showCommentWindow) {539 width = (window.innerWidth * 5.0) / 6;540 }541 return (542 <FullScreen handle={fullScreenHandle}>543 <div544 style={{545 width: "100%",546 margin: "auto",547 marginTop: 10,548 background: "white",549 }}550 >551 <Grid container>552 <Grid item xs={plotProperties.showCommentWindow ? 10 : 12}>553 <Plots554 dataList={displayDataList}555 refreshPlots={state.refreshPlots}556 plotProperties={plotProperties}557 displayTimeRange={state.displayTimeRange}558 channelNames={state.dataList.channelNames}559 handleRefreshPlots={handleRefreshPlots}560 enablePSGScoring={state.enablePSGScoring}561 startEpochNumber={epochNumber}562 scores={state.dataList.PSGScores}563 width={width}564 />565 </Grid>566 {plotProperties.showCommentWindow && (567 <Grid item xs={2}>568 <div style={{ height: "100%", width: "100%" }}>569 <DataGrid570 components={{ NoRowsOverlay }}571 rows={comments}572 columns={columns}573 pageSize={100}574 rowsPerPageOptions={[100]}575 density="compact"576 hideFooter={comments.length <= 100}577 hideFooterSelectedRowCount={true}578 hideFooterRowCount={true}579 isCellEditable={false}580 onRowDoubleClick={handleCommentWindowDoubleClick}581 ></DataGrid>582 </div>583 </Grid>584 )}585 </Grid>586 <XAxis587 disabled={fullScreenHandle.active}588 displayTimeRange={state.displayTimeRange}589 displayInterval={state.displayInterval}590 dataTimeRange={state.dataTimeRange}591 enablePSGScoring={state.enablePSGScoring}592 handleIntervalChange={handleIntervalChange}593 scores={state.dataList.PSGScores}594 handleEpochClick={handleEpochClick}595 width={width}596 />597 <div style={{ display: "inline-block" }}>598 <IconButton599 disabled={fullScreenHandle.active}600 style={{ marginTop: buttonIconMarginTop }}601 onClick={handleMenuClick}602 >603 <MenuIcon />604 </IconButton>605 <IconButton606 style={{ marginTop: buttonIconMarginTop }}607 onClick={608 fullScreenHandle.active609 ? fullScreenHandle.exit610 : fullScreenHandle.enter611 }612 >613 <FullscreenIcon />614 </IconButton>615 {state.enablePSGScoring && (616 <Tooltip title="Save scoring to server">617 <IconButton618 style={{ marginTop: buttonIconMarginTop }}619 disabled={!state.dataList.PSGScores.isDirty}620 onClick={handleSaveScoring}621 >622 <Save />623 </IconButton>624 </Tooltip>625 )}626 {state.enablePSGScoring && (627 <Tooltip title="View results">628 <IconButton629 style={{ marginTop: buttonIconMarginTop }}630 onClick={handleViewScoringReport}631 >632 <AssessmentIcon />633 </IconButton>634 </Tooltip>635 )}636 {state.enablePSGScoring && (637 <Tooltip title="Undo last scoring event">638 <IconButton639 style={{ marginTop: buttonIconMarginTop }}640 onClick={handleUndoScoring}641 disabled={!state.dataList.PSGScores.canUndo}642 >643 <UndoIcon />644 </IconButton>645 </Tooltip>646 )}647 {state.enablePSGScoring && (648 <Tooltip title="Redo last scoring event">649 <IconButton650 style={{ marginTop: buttonIconMarginTop }}651 onClick={handleRedoScoring}652 disabled={!state.dataList.PSGScores.canRedo}653 >654 <RedoIcon />655 </IconButton>656 </Tooltip>657 )}658 {!state.enablePSGScoring && (659 <IconButton660 style={{ marginTop: buttonIconMarginTop }}661 onClick={handleFastRewind}662 disabled={663 stateRef.current.playingSpeed > 0 &&664 stateRef.current.playingSpeed < 0.2665 }666 >667 <FastRewindIcon />668 </IconButton>669 )}670 {!state.enablePSGScoring && (671 <IconButton672 style={{ marginTop: buttonIconMarginTop }}673 onClick={handlePlayPause}674 >675 {state.playingSpeed > 0 ? <PauseIcon /> : <PlayArrowIcon />}676 </IconButton>677 )}678 {!state.enablePSGScoring && (679 <IconButton680 style={{ marginTop: buttonIconMarginTop }}681 onClick={handleFastForward}682 disabled={683 stateRef.current.playingSpeed > 0 &&684 stateRef.current.playingSpeed > 5685 }686 >687 <FastForwardIcon />688 </IconButton>689 )}690 <PropertyMenu691 openMenu={state.openMenu}692 plotProperties={plotProperties}693 channels={state.dataList.channelNames}694 isLiveData={state.isLiveData}695 hasScoringReport={state.dataList.PSGScores}696 handleMenuClose={handleMenuClose}697 handleChannelCheckboxChange={handleChannelCheckboxChange}698 handleColorChange={handleBackgroundColorChange}699 handlePreferenceChange={handlePreferenceChange}700 handleShowGridChange={handleShowGridChange}701 handleShowChannelScaleChange={handleShowChannelScaleChange}702 handleShowCommentWindowChange={handleShowCommentWindowChange}703 handleShowChannelLabelChange={handleShowChannelLabelChange}704 handleWatermarkChange={handleWatermarkChange}705 handlePSGScoringChange={handlePSGScoringChange}706 handleRespiratoryChannelChange={handleRespiratoryChannelChange}707 handleChannelLabelFontSizeChange={708 handleChannelLabelFontSizeChange709 }710 handleViewScoringReport={handleViewScoringReport}711 />712 </div>713 <div714 style={{715 width: state.enablePSGScoring716 ? timeNavigationWidth - 45717 : timeNavigationWidth,718 display: "inline-block",719 }}720 >721 <TimeNavigation722 dataTimeRange={state.dataTimeRange}723 displayInterval={state.displayInterval}724 sliderValue={state.sliderValue}725 sliderMax={sliderMax}726 handleSliderChange={handleSliderChange}727 progressBarValue={progressBarValue}728 isLiveData={state.isLiveData}729 enablePSGScoring={state.enablePSGScoring}730 scores={state.dataList.PSGScores}731 width={732 state.enablePSGScoring733 ? timeNavigationWidth - 45734 : timeNavigationWidth735 }736 />737 </div>738 <Modal open={state.openReport} onClose={handleReportClose}>739 <PSGScoreReport scores={state.dataList.PSGScores} />740 </Modal>741 </div>742 </FullScreen>743 );744 } else if (state.hasFetchedAllData) {745 return (746 <MessagedProgress message="No data is available." hideProgress={true} />747 );748 } else if (state.errorMessage !== "") {749 return (750 <MessagedProgress message={state.errorMessage} hideProgress={true} />751 );752 } else {753 return <MessagedProgress message="Retrieving time series data ..." />;754 }755 function updateDisplayInterval(newInterval) {756 let newValue = newInterval;757 if (newValue === 0) {758 newValue = state.dataList.timeRange.duration;759 }760 const currentDisplayMiddleTime =761 state.displayTimeRange.startTime -762 state.dataList.timeRange.startTime +763 0.5 * state.displayTimeRange.duration;764 const startTimeFactor = Math.max(765 0,766 Math.min(767 state.dataList.timeRange.duration / newValue - 1,768 Math.round(currentDisplayMiddleTime / newValue - 0.5)769 )770 );771 let newDisplayTimeRange = new TimeRange();772 newDisplayTimeRange.startTime =773 state.dataList.timeRange.startTime + startTimeFactor * newValue;774 newDisplayTimeRange.endTime = newDisplayTimeRange.startTime + newValue;775 const newSlideValue =776 newDisplayTimeRange.startTime - state.dataList.timeRange.startTime;777 setState({778 ...stateRef.current,779 displayInterval: newInterval,780 sliderValue: newSlideValue,781 displayTimeRange: newDisplayTimeRange,782 refreshPlots: true,783 });784 }785 async function fetchData(isFirst = false) {786 if (stateRef.current.hasFetchedAllData) {787 return;788 }789 console.log("calling server");790 let query = { DataIDs: studyInfo.dataIDs };791 if (isFirst) {792 query = { DataIDs: studyInfo.dataIDs, RetrievalPreference: 4 };793 }794 const response = await ServerClient.getTimeSeriesData(query);795 if (response.status === 200) {796 updateData(response.data);797 if (response.data.length === 0) {798 setState({799 ...stateRef.current,800 hasFetchedAllData: true,801 isLiveData: false,802 });803 } else {804 setTimeout(() => {805 fetchData();806 }, 10);807 }808 } else if (response.status === 401) {809 history.push(window.$websiteAlias + "signin");810 setSessionInfo({811 userName: "",812 isLoggedIn: false,813 pageTitle: "",814 dataIDs: [0],815 });816 } else {817 setState({818 ...stateRef.current,819 errorMessage:820 "Unable to retrieve time series data (" +821 response.status +822 "): " +823 response.errorMessage,824 });825 }826 }827 function NoRowsOverlay() {828 return (829 <div style={{ marginTop: 45 }}>830 <Typography align="center" variant="body2">831 No Annotation Found832 </Typography>833 </div>834 );835 }836 function updateData(newData) {837 if (newData && newData.length > 0) {838 newData.forEach((dataChunk) => {839 const index = studyInfo.dataIDs.indexOf(dataChunk.dataID);840 if (index >= 0 && studyInfo.deviceIDs.length > index) {841 dataChunk.deviceID = studyInfo.deviceIDs[index];842 } else {843 dataChunk.deviceID = -1;844 }845 });846 if (stateRef.current.dataList) {847 const currentChannelCount =848 stateRef.current.dataList.channelNames.length;849 const newDataList = stateRef.current.dataList;850 newDataList.add(newData);851 let dataTimeRange = stateRef.current.dataTimeRange;852 let displayTimeRange = stateRef.current.displayTimeRange;853 let refreshPlots = false;854 if (stateRef.current.isLiveData) {855 if (856 displayTimeRange.endTime >857 dataTimeRange.endTime - stateRef.current.displayInterval858 ) {859 dataTimeRange.endTime = newDataList.timeRange.endTime;860 displayTimeRange.endTime = dataTimeRange.endTime;861 displayTimeRange.startTime =862 displayTimeRange.endTime - stateRef.current.displayInterval;863 if (864 displayTimeRange.startTime <865 stateRef.current.dataTimeRange.startTime866 ) {867 displayTimeRange.startTime =868 stateRef.current.dataTimeRange.startTime;869 }870 refreshPlots = true;871 } else {872 dataTimeRange.endTime = newDataList.timeRange.endTime;873 }874 }875 const newChannelCount = stateRef.current.dataList.channelNames.length;876 if (currentChannelCount !== newChannelCount) {877 refreshPlots = true;878 }879 const sliderValue =880 displayTimeRange.startTime - stateRef.current.dataTimeRange.startTime;881 setState({882 ...stateRef.current,883 dataList: newDataList,884 dataTimeRange: dataTimeRange,885 displayTimeRange: displayTimeRange,886 sliderValue: sliderValue,887 refreshPlots: refreshPlots,888 });889 } else {890 const newDataList = new ChannelDataList(newData);891 let displayTimeRange = new TimeRange();892 displayTimeRange.startTime = newDataList.timeRange.startTime;893 displayTimeRange.endTime = newDataList.timeRange.endTime;894 if (newDataList.timeRange.duration > stateRef.current.displayInterval)895 if (stateRef.current.isLiveData) {896 displayTimeRange.startTime =897 displayTimeRange.endTime - stateRef.current.displayInterval;898 } else {899 displayTimeRange.endTime =900 displayTimeRange.startTime + stateRef.current.displayInterval;901 }902 const sliderValue =903 displayTimeRange.startTime - stateRef.current.dataTimeRange.startTime;904 let dataTimeRange = stateRef.current.dataTimeRange;905 if (stateRef.current.isLiveData) {906 dataTimeRange.endTime = newDataList.timeRange.endTime;907 }908 setState({909 ...stateRef.current,910 dataTimeRange: dataTimeRange,911 sliderValue: sliderValue,912 displayTimeRange: displayTimeRange,913 dataList: newDataList,914 refreshPlots: true,915 });916 }917 }918 }...

Full Screen

Full Screen

App.js

Source:App.js Github

copy

Full Screen

1import { v4 as uuidv4 } from 'uuid';2import './App.css';3import Piece from './components/Piece';4import { useEffect, useState, useRef } from 'react';5import {6 generatePiece,7 getRotation,8 piecePattern,9 kickTable,10} from './tetrisUtils';11function App() {12 const stateRef = useRef();13 const [board, setBoard] = useState(14 new Array(22).fill('0000000000').fill('1111111111', 21)15 );16 const [lineCount, setLineCount] = useState(0);17 const [isRunning, setIsRunning] = useState(false);18 const [next, setNext] = useState(null);19 const [hold, setHold] = useState(null);20 /* Controls */21 useEffect(() => {22 window.addEventListener('keyup', handleKeyUp);23 window.addEventListener('keydown', handleKeyDown);24 let prevFrame;25 let gravity = 0;26 let isDropped = false;27 let boost = 1;28 let pieceBag = [0, 1, 2, 3, 4, 5, 6].sort(() => Math.random() - 0.5);29 let pieceQueue;30 let canChange = true;31 let altPiece = null;32 let level = 1;33 randomPiece();34 stateRef.current = {35 piece: { ...generatePiece(pieceQueue[0]) },36 board: new Array(22).fill('0000000000').fill('1111111111', 21),37 baseBoard: new Array(22).fill('0000000000').fill('1111111111', 21),38 };39 requestAnimationFrame(frame);40 function randomPiece() {41 let newPiece;42 if (pieceBag.length === 1) {43 newPiece = pieceBag[0];44 pieceBag = [0, 1, 2, 3, 4, 5, 6].sort(() => Math.random() - 0.5);45 } else {46 newPiece = pieceBag.shift();47 }48 setNext(pieceBag[0]);49 pieceQueue = [newPiece, pieceBag[0]];50 }51 function handleKeyDown(e) {52 switch (e.code) {53 case 'ArrowDown':54 if (!e.repeat) {55 boost = 20;56 }57 break;58 case 'ArrowLeft':59 if (60 stateRef.current.piece.parts.find((e) => e.x <= 0) ||61 stateRef.current.piece.parts.find(62 (e) => stateRef.current.baseBoard[e.y][e.x - 1] !== '0'63 )64 )65 return;66 stateRef.current.piece.parts.forEach((e) => (e.x = e.x - 1));67 stateRef.current.piece.global.x--;68 break;69 case 'ArrowRight':70 if (71 stateRef.current.piece.parts.find((e) => e.x >= 9) ||72 stateRef.current.piece.parts.find(73 (e) => stateRef.current.baseBoard[e.y][e.x + 1] !== '0'74 )75 )76 return;77 stateRef.current.piece.parts.forEach((e) => (e.x = e.x + 1));78 stateRef.current.piece.global.x++;79 break;80 case 'KeyW':81 {82 let newPattern = stateRef.current.piece.pattern.map((e, i) =>83 stateRef.current.piece.pattern84 .map((el) => el[el.length - 1 - i])85 .join('')86 );87 let coordinates = getRotation(88 newPattern,89 stateRef.current.piece.global.x,90 stateRef.current.piece.global.y91 );92 let i = 0;93 while (94 coordinates.find(95 (e) => stateRef.current.baseBoard[e.y][e.x] !== '0'96 )97 ) {98 // console.log(kickTable[stateRef.current.piece.rotation][i][0])99 coordinates.forEach(100 (e) =>101 (e.x = e.x + kickTable[stateRef.current.piece.rotation][i][0])102 );103 // coordinates.forEach(e => e.y = e.y + kickTable[stateRef.current.piece.rotation][i][1])104 // stateRef.current.piece.global.x += kickTable[stateRef.current.piece.rotation][i][0]105 // stateRef.current.piece.global.y += kickTable[stateRef.current.piece.rotation][i][1]106 i++;107 }108 if (i > 4) return;109 stateRef.current.piece.parts = coordinates;110 stateRef.current.piece.pattern = newPattern;111 stateRef.current.piece.rotation !== 3112 ? stateRef.current.piece.rotation++113 : (stateRef.current.piece.rotation = 0);114 }115 break;116 case 'KeyQ':117 if (canChange) {118 canChange = false;119 if (altPiece != null) {120 stateRef.current.piece = { ...generatePiece(altPiece) };121 altPiece = pieceQueue[0];122 setHold(pieceQueue[0]);123 } else {124 stateRef.current.piece = { ...generatePiece(pieceQueue[1]) };125 altPiece = pieceQueue[0];126 setHold(pieceQueue[0]);127 randomPiece();128 }129 }130 break;131 case 'ArrowUp':132 {133 let newPattern = stateRef.current.piece.pattern.map((e, i) =>134 stateRef.current.piece.pattern135 .map(136 (el, y) =>137 stateRef.current.piece.pattern[138 stateRef.current.piece.pattern.length - 1 - y139 ][i]140 )141 .join('')142 );143 let coordinates = getRotation(144 newPattern,145 stateRef.current.piece.global.x,146 stateRef.current.piece.global.y147 );148 let tmp = coordinates.map((e) => ({ ...e }));149 let globalX = 0;150 let globalY = 0;151 let i = -1;152 console.log('rotation', stateRef.current.piece.rotation);153 while (154 tmp.find((e) => stateRef.current.baseBoard[e.y][e.x] !== '0')155 ) {156 console.log(157 tmp.find((e) => stateRef.current.baseBoard[e.y][e.x] !== '0')158 );159 i++;160 console.log(161 'y',162 kickTable[stateRef.current.piece.rotation][i][1]163 );164 tmp = [165 ...coordinates.map((e) => ({166 ...e,167 x: e.x + kickTable[stateRef.current.piece.rotation][i][0],168 y: e.y + kickTable[stateRef.current.piece.rotation][i][1],169 })),170 ];171 globalX = kickTable[stateRef.current.piece.rotation][i][0];172 globalY = kickTable[stateRef.current.piece.rotation][i][1];173 console.log('i', i);174 }175 console.log('apres', tmp);176 stateRef.current.piece.global.x += globalX;177 stateRef.current.piece.global.y += globalY;178 stateRef.current.piece.parts = tmp;179 stateRef.current.piece.pattern = newPattern;180 stateRef.current.piece.rotation !== 3181 ? stateRef.current.piece.rotation++182 : (stateRef.current.piece.rotation = 0);183 }184 break;185 default:186 break;187 }188 }189 function handleKeyUp(e) {190 if (e.code === 'ArrowDown') {191 boost = 1;192 }193 }194 function frame(timestamp) {195 let delta = (timestamp - prevFrame) / 1e3 || 0;196 gravity += delta * boost;197 prevFrame = timestamp;198 if (gravity > 1) {199 stateRef.current.baseBoard.forEach((e, i) => {200 if (e === 'xxxxxxxxxx') {201 stateRef.current.baseBoard.splice(i, 1);202 stateRef.current.baseBoard.unshift('0000000000');203 }204 });205 if (isDropped) {206 randomPiece();207 stateRef.current.piece = { ...generatePiece(pieceQueue[0]) };208 render();209 isDropped = false;210 }211 if (212 stateRef.current.piece.parts.find((e) => e.y >= 20) ||213 stateRef.current.piece.parts.find(214 (e) => stateRef.current.baseBoard[e.y + 1][e.x] !== '0'215 )216 ) {217 canChange = true;218 stateRef.current.board.forEach((e, i) => {219 if (!e.includes('0') && i !== 21) {220 stateRef.current.board[i] = 'xxxxxxxxxx';221 setLineCount((prev) => prev + 1);222 level++;223 }224 });225 isDropped = true;226 stateRef.current.baseBoard = [...stateRef.current.board];227 setBoard([...stateRef.current.baseBoard]);228 } else {229 stateRef.current.piece.parts.forEach((e) => (e.y = e.y + 1));230 stateRef.current.piece.global.y++;231 }232 gravity = 0;233 }234 render();235 requestAnimationFrame(frame);236 }237 function render() {238 let currBoard = [...stateRef.current.baseBoard];239 let ghostPiece = [...stateRef.current.piece.parts.map((e) => ({ ...e }))];240 while (!ghostPiece.find((e) => currBoard[e.y + 1][e.x] !== '0')) {241 ghostPiece = [...ghostPiece.map((e) => ({ ...e, y: e.y + 1 }))];242 }243 ghostPiece.forEach((e, i) => {244 let cell = currBoard[e.y].split('');245 cell[e.x] = stateRef.current.piece.class.toUpperCase();246 currBoard[e.y] = cell.join('');247 });248 stateRef.current.piece.parts.forEach((e, i) => {249 let cell = currBoard[e.y].split('');250 cell[e.x] = stateRef.current.piece.class;251 currBoard[e.y] = cell.join('');252 });253 stateRef.current.board = [...currBoard];254 setBoard([...currBoard]);255 }256 return () => {257 window.removeEventListener('keyup', handleKeyUp);258 window.removeEventListener('keydown', handleKeyDown);259 cancelAnimationFrame(frame);260 };261 }, []);262 return (263 <div className="app">264 <div className="side">265 <p className="infos">Hold</p>266 <Piece piece={piecePattern[hold]} />267 <p className="infos">Lines: {lineCount}</p>268 </div>269 <div className="board">270 {board.map(271 (cell, i) =>272 i !== 21 &&273 i !== 0 && (274 <div key={uuidv4()} className={`row ${cell}`}>275 {cell276 .split('')277 .map((e) =>278 e === '0' ? (279 <div key={uuidv4()} className={`cell`}></div>280 ) : (281 <div key={uuidv4()} className={`${e}-block`}></div>282 )283 )}284 </div>285 )286 )}287 </div>288 <div className="side">289 <p className="infos">Next</p>290 <Piece piece={piecePattern[next]} />291 </div>292 </div>293 );294}...

Full Screen

Full Screen

index.jsx

Source:index.jsx Github

copy

Full Screen

1import React, { useEffect, useRef } from 'react';2import Curtain from './Curtain';3import Round from './Round';4import { useKeyEvent } from '../../hooks/useKeyEvent';5import { useStore } from '../../hooks/useStore';6import { decrement, increment } from '../../utils/number';7import { postMessage } from '../../workers';8import EVENTS from '../../constants/events';9import GAME from '../../constants/game';10import MESSAGES from '../../constants/messages';11import SETTINGS from '../../constants/settings';12import TANK from '../../constants/tank';13function selector({ game: { stage, status }, session: { setSession, ...session } }) {14 return { game: { stage, status }, session };15}16export default function Game() {17 const state = useStore(selector);18 const stateRef = useRef(state);19 useEffect(20 function effect() {21 stateRef.current = state;22 },23 [state],24 );25 function getStringStage(stage) {26 const round = stage + 1;27 return round < 10 ? ` ${round}` : String(round);28 }29 useKeyEvent({30 key: SETTINGS.KEYS.UP,31 type: EVENTS.DOWN,32 listener() {33 switch (stateRef.current.game.status) {34 case GAME.STATUS.WAITING: {35 postMessage({36 type: MESSAGES.SET_STAGE,37 payload: increment(stateRef.current.game.stage),38 });39 break;40 }41 case GAME.STATUS.RUNNING: {42 postMessage({43 type: MESSAGES.SET_DIRECTION,44 payload: {45 direction: TANK.DIRECTION.NORTH,46 session: stateRef.current.session,47 },48 });49 break;50 }51 default: {52 break;53 }54 }55 },56 });57 useKeyEvent({58 key: SETTINGS.KEYS.DOWN,59 type: EVENTS.DOWN,60 listener() {61 switch (stateRef.current.game.status) {62 case GAME.STATUS.WAITING: {63 postMessage({64 type: MESSAGES.SET_STAGE,65 payload: decrement(stateRef.current.game.stage),66 });67 break;68 }69 case GAME.STATUS.RUNNING: {70 postMessage({71 type: MESSAGES.SET_DIRECTION,72 payload: {73 direction: TANK.DIRECTION.SOUTH,74 session: stateRef.current.session,75 },76 });77 break;78 }79 default: {80 break;81 }82 }83 },84 });85 useKeyEvent({86 key: SETTINGS.KEYS.LEFT,87 type: EVENTS.DOWN,88 listener() {89 switch (stateRef.current.game.status) {90 case GAME.STATUS.RUNNING: {91 postMessage({92 type: MESSAGES.SET_DIRECTION,93 payload: {94 direction: TANK.DIRECTION.WEST,95 session: stateRef.current.session,96 },97 });98 break;99 }100 default: {101 break;102 }103 }104 },105 });106 useKeyEvent({107 key: SETTINGS.KEYS.RIGHT,108 type: EVENTS.DOWN,109 listener() {110 switch (stateRef.current.game.status) {111 case GAME.STATUS.RUNNING: {112 postMessage({113 type: MESSAGES.SET_DIRECTION,114 payload: {115 direction: TANK.DIRECTION.EAST,116 session: stateRef.current.session,117 },118 });119 break;120 }121 default: {122 break;123 }124 }125 },126 });127 useKeyEvent({128 key: SETTINGS.KEYS.UP,129 type: EVENTS.UP,130 listener() {131 switch (stateRef.current.game.status) {132 case GAME.STATUS.RUNNING: {133 postMessage({134 type: MESSAGES.SET_DIRECTION,135 payload: {136 session: stateRef.current.session,137 },138 });139 break;140 }141 default: {142 break;143 }144 }145 },146 });147 useKeyEvent({148 key: SETTINGS.KEYS.DOWN,149 type: EVENTS.UP,150 listener() {151 switch (stateRef.current.game.status) {152 case GAME.STATUS.RUNNING: {153 postMessage({154 type: MESSAGES.SET_DIRECTION,155 payload: {156 session: stateRef.current.session,157 },158 });159 break;160 }161 default: {162 break;163 }164 }165 },166 });167 useKeyEvent({168 key: SETTINGS.KEYS.LEFT,169 type: EVENTS.UP,170 listener() {171 switch (stateRef.current.game.status) {172 case GAME.STATUS.RUNNING: {173 postMessage({174 type: MESSAGES.SET_DIRECTION,175 payload: {176 session: stateRef.current.session,177 },178 });179 break;180 }181 default: {182 break;183 }184 }185 },186 });187 useKeyEvent({188 key: SETTINGS.KEYS.RIGHT,189 type: EVENTS.UP,190 listener() {191 switch (stateRef.current.game.status) {192 case GAME.STATUS.RUNNING: {193 postMessage({194 type: MESSAGES.SET_DIRECTION,195 payload: {196 session: stateRef.current.session,197 },198 });199 break;200 }201 default: {202 break;203 }204 }205 },206 });207 useKeyEvent({208 key: SETTINGS.KEYS.START,209 type: EVENTS.DOWN,210 listener() {211 postMessage({212 type: MESSAGES.SET_STATE,213 payload: {214 session: stateRef.current.session,215 status: stateRef.current.game.status === GAME.STATUS.WAITING216 ? GAME.STATUS.RUNNING217 : GAME.STATUS.WAITING,218 },219 });220 },221 });222 useKeyEvent({223 key: SETTINGS.KEYS.FIRE,224 type: EVENTS.DOWN,225 listener() {226 if (stateRef.current.game.status !== GAME.STATUS.RUNNING) return;227 postMessage({228 type: MESSAGES.FIRE,229 payload: {230 session: stateRef.current.session,231 },232 });233 },234 });235 switch (state.game.status) {236 case GAME.STATUS.WAITING: {237 return (238 <Curtain stringStage={getStringStage(state.game.stage)} />239 );240 }241 case GAME.STATUS.RUNNING: {242 return (243 <Round game={state.game} stringStage={getStringStage(state.game.stage)} />244 );245 }246 default: {247 return null;248 }249 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1import { stateRef } from 'storybook-root'2const {state, setState} = stateRef('test')3import { stateRef } from 'storybook-root'4const {state, setState} = stateRef('test')5import { stateRef } from 'storybook-root'6const {state, setState} = stateRef('test')7import { stateRef } from 'storybook-root'8const {state, setState} = stateRef('test')9import { stateRef } from 'storybook-root'10const {state, setState} = stateRef('test')11import { stateRef } from 'storybook-root'12const {state, setState} = stateRef('test')13import { stateRef } from 'storybook-root'14const {state, setState} = stateRef('test')15import { stateRef } from 'storybook-root'16const {state, setState} = stateRef('test')17import { stateRef } from 'storybook-root'18const {state, setState} = stateRef('test')19import { stateRef } from 'storybook-root'20const {state, setState} = stateRef('test')21import { stateRef } from 'storybook-root'22const {state, setState} = stateRef('test')23import { stateRef } from 'storybook-root'24const {state, setState} = stateRef('test')25import { stateRef } from 'storybook-root'26const {state, setState} = stateRef('test')27import { stateRef } from 'storybook-root'28const {state, setState} = stateRef('test')29import { stateRef } from 'storybook-root'30const {state, setState} = stateRef('test')

Full Screen

Using AI Code Generation

copy

Full Screen

1import { stateRef } from 'storybook-root';2import { storiesOf } from '@storybook/react';3import React from 'react';4import { Button } from '../src';5storiesOf('Button', module)6 .add('with text', () => (7 <Button onClick={stateRef().action('clicked')}>8 .add('with some emoji', () => (9 <Button onClick={stateRef().action('clicked')}>10 ));11import React from 'react';12import { stateRef } from 'storybook-root';13const Button = ({ onClick, children }) => (14 <button onClick={onClick}>15 {children}16);17export default Button;18import React from 'react';19import { storiesOf } from '@storybook/react';20import { action } from '@storybook/addon-actions';21import { stateRef } from 'storybook-root';22import { Button } from './Button';23storiesOf('Button', module)24 .add('with text', () => (25 <Button onClick={stateRef().action('clicked')}>26 .add('with some emoji', () => (27 <Button onClick={stateRef().action('clicked')}>28 .add('with some emoji and action', () => (29 <Button onClick={stateRef().action('clicked')}>30 ));31import React from 'react';32import { storiesOf } from '@storybook/react';33import { action } from '@storybook/addon-actions';34import { stateRef } from 'storybook-root';35import { Button } from './Button';36storiesOf('Button', module)37 .add('with text', () => (38 <Button onClick={state

Full Screen

Using AI Code Generation

copy

Full Screen

1import { useStorybookState } from 'storybook-addon-root';2const Test = () => {3 const { state } = useStorybookState();4 return <div>state: {state}</div>;5};6export default Test;7import React from 'react';8import Test from './test';9import { withStorybookRoot } from 'storybook-addon-root';10export default {11};12export const test = () => <Test />;13import { addDecorator } from '@storybook/react';14import { withStorybookRoot } from 'storybook-addon-root';15addDecorator(withStorybookRoot);16import { addons } from '@storybook/addons';17import { withStorybookRoot } from 'storybook-addon-root';18addons.setConfig({19});20import 'storybook-addon-root/register';21import { configure } from '@storybook/react';22import requireContext from 'require-context.macro';23const req = requireContext('../src', true, /\.stories\.js$/);24function loadStories() {25 req.keys().forEach(filename => req(filename));26}27configure(loadStories, module);28import { addDecorator } from '@storybook/react';29import { withStorybookRoot } from 'storybook-addon-root';30addDecorator(withStorybookRoot);31import { addons } from '@storybook/addons';32import { withStorybookRoot } from 'storybook-addon-root';33addons.setConfig({34});35import 'storybook-addon-root/register';36import { configure } from '@storybook/react';37import requireContext from 'require-context.macro';38const req = requireContext('../src', true, /\.stories\.js$/);39function loadStories() {40 req.keys().forEach(filename => req(filename));41}42configure(loadStories, module);43import { addDecorator } from

Full Screen

Using AI Code Generation

copy

Full Screen

1import {stateRef} from 'storybook-root'2stateRef.set('test', 'test')3import {stateRef} from 'storybook-root'4import {stateRef} from 'storybook-root'5import {stateRef} from 'storybook-root'6import {stateRef} from 'storybook-root'7import {stateRef} from 'storybook-root'8import {stateRef} from 'storybook-root'9import {stateRef} from 'storybook-root'10import {stateRef} from 'storybook-root'11import {stateRef} from 'storybook-root'12import {stateRef} from 'storybook-root'13import {stateRef} from 'storybook-root'14import {stateRef} from 'storybook-root'

Full Screen

Using AI Code Generation

copy

Full Screen

1const { stateRef } = require('storybook-root');2const state = stateRef('test');3const { stateRef } = require('storybook-root');4const state = stateRef('test');5const { stateRef } = require('storybook-root');6const state = stateRef('test2');7const { stateRef } = require('storybook-root');8const state = stateRef('test2');9const { stateRef } = require('storybook-root');10const state = stateRef('test3');11const { stateRef } = require('storybook-root');12const state = stateRef('test3');13const { stateRef } = require('storybook-root');14const state = stateRef('test4');15const { stateRef } = require('storybook-root');16const state = stateRef('test4');17const { stateRef } = require('storybook-root');18const state = stateRef('test5');19const { stateRef } = require('storybook-root');20const state = stateRef('test5

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run storybook-root automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful