How to get className to work with dynamic input from map() - reactjs

Trying to render some buttons dynamically, cant get the styling to work.
I have my btnList -
const btnList= [
"FirsBtn",
"SecondBtn",
"ThirdBtn",
"ForthBtn",
"FifthBtn",
"SixthBtn",
];
I have my map function -
const RenderBtns = (btnList) => {
return btnList.map((btn) => {
return (
<button className={`classes.${btn}`} name={btn} onClick={onClickHandler}>{btn}</button>
);
});
};
And, I have my btn.module.css, with
.FirstBtn {background-color: #662626;}
.SecondBtn {background-color: #b0bf76;}
.ThirdBtn {background-color: #6b8327;}
.ForthBtn {background-color: #6a5938;}
.FifthBtn {background-color: #b0bf76;}
.SixthBtn {background-color: #1fa593;}
The styling, doesn't work.
This - className={classes.FirstBtn} works.
This - className={`${classes.SecondBtn}`} works.
This - className={`${classes}.${btn}`} Doesn't.
The buttons themselves, the clicks, everything else - works.
I went through all of the combinations, and I can't seem to get it to work.
Even though I get the main idea behind literal, and object,
at this point I'm just beat-up.
I want to get the className to accept the dynamic "btn"
any way to make it happen?
Help, Please, and thank you.

Almost gave up on google, and than I stumbled upon this
Answer
It was actually the second one that worked for me,
but n one the less - it worked.
className={classes[`${item}Btn`]}

Related

How to use styled-system responsive props

Can I use the styled-system to achieve something like this?
<MyComponent
backgroundImage={{
default: "https://placekitten.com/380/80",
sm: "https://placekitten.com/340/80"
}}
/>
or this (because I know it can be done this way too with other "style props" such as width, but I prefer to use an object with keys):
<MyComponent
backgroundImage={[
"https://placekitten.com/300/80",
"https://placekitten.com/500/80"
]}
/>
I think the code examples above are self-descriptive and they follow the library's pattern but just to be clear, I'm mapping the values (image sources) to the breakpoints (default and next one up).
For example, this works out of the box:
<Box
width={[
default: 1,
sm: 1/3,
]}
/>
The output is something like this:
.QWojd {
width: 100%;
}
#media screen and (min-width: 24em) {
.QWojd {
width: 33.33333333333333%;
}
}
I've been looking into the source code and this part here makes me think it should work with backgroundImage too:
Sadly, it doesn't work, and the result is a stringified object (or concatenated array values) in the CSS output.
I can't think of how the variant function would be useful here as people have suggested. I've tried to use the system function but I just can't understand the documentation. The ResponsiveValue type gives me a hint but I feel like crawling in the dark when I try to understand the internals.
Ultimately, I'd like to use the "breakpoints object" (or array) with whatever custom prop I feel like, like this:
<Box
myProp={[
default: 'foo',
sm: 'bar',
]}
/>
Note: I've learned (from experience) that you can just use the "breakpoints array" version (without setting breakpoints in a theme and passing it to a provider) and that will map the value to the first 2 default breakpoints (not sure where they come from) but if you want to use an object with keys you need to use a ThemeProvider with a theme object with your own breakpoints.
Note 2: I can understand the styled-system documentation up to this point: https://styled-system.com/custom-props. When I arrive here I feel like this is what I'm looking for but I can't understand the example, the explanation confuses me even more and I can't find any examples on the web.
Note 3: Spectrum Chat has a styled-system channel and the library author is in there but sadly I haven't been able to send any messages there (constant network error)
Examples
Ok, so according to the docs (https://styled-system.com/custom-props/), in order to create a custom prop (or in this case, replace the existing one) you should use the system utility. Since I'm not a user of this library (styled-system), I'm not 100% sure that this is correct approach, but I tested on top of your example code and it seems to work as you wanted.
The component declaration (it also works with objects like you wanted) with an array:
<ResponsiveImageBox
color="white"
backgroundImage={[
"https://placekitten.com/300/80",
"https://placekitten.com/500/80"
]}
>
Box 8
</ResponsiveImageBox>
with objects:
<ResponsiveImageBox
color="white"
backgroundImage={{
default: "https://placekitten.com/300/80",
sm: "https://placekitten.com/500/80"
}}
>
Box 8
</ResponsiveImageBox>
And this is the component code:
export const ResponsiveImageBox = styled(Box)(
({ myCustomProp }) => {
return css`
${system({
backgroundImage: {
property: "backgroundImage",
transform: value => `url(${value})`
}
})}
`
});
As you can see on examples 4, 5 and 8 (https://stackblitz.com/edit/styled-system-mccqje?file=Examples.tsx), I also did it for the border-radius attribute with a simple prop renaming and just specifying what css attribute I wanted to change (property), so no need to add transform as the value will remain the same.
export const ExtendedBox2 = styled(Box)<ExtendedBoxProps>`
background-position: center;
${system({
myCustomProp: {
property: "border-radius"
}
})}
`;
Have a look and see if this is what you were looking for! :)
I know you already marked it as solved, and Eduardo's approach definitely works. However another way you can do it "out of the box" is to use aliases so that you can use objects instead of arrays (source: https://styled-system.com/responsive-styles/):
// theme.js
const breakpoints = ['40em', '52em', '64em', '80em']
// aliases
breakpoints.sm = breakpoints[0]
breakpoints.md = breakpoints[1]
breakpoints.lg = breakpoints[2]
breakpoints.xl = breakpoints[3]
export default {
breakpoints,
}
// ResponsiveImageBox.js
<ResponsiveImageBox
color="white"
backgroundImage={{
md: "https://placekitten.com/300/80",
sm: "https://placekitten.com/500/80"
}}
>
Box 8
</ResponsiveImageBox>

Customization of the Height of a Pivot Item Link Line in Fluent UI

I'm trying to increase the height of the line on the selected Pivot item link in Microsoft's Fluent UI using React.
Here's a screenshot for the purposes of clarification:
The orange arrow is pointing to the blue line of which I would like to increase the height.
I have tried setting the styles attribute of the Pivot component but thus far have been unsuccessful. Here's some code
const pivotStyles: Partial<IStyleSet<IPivotStyles>> = {
link: { ? },
linkContent: { ? }
};
<Pivot styles={pivotStyles} linkSize={PivotLinkSize.large}>
<PivotItem headerText="Zane"></PivotItem>
<PivotItem headerText="Kai"></PivotItem>
<PivotItem headerText="Jay"></PivotItem>
</Pivot>
I have experimented with different attributes of both link and linkContent but haven't found a way yet. I believe what I'm trying to do is manipulate the IStyle interface but I can't find details of the attributes of it. The link there is very vague. For example, what are all the available attributes of link, linkContent, etc.?
Any thoughts on this would be most appreciated!
Thank you.
Turns out I was on the right track and just needed the exact fields. Here's what ended up working:
const pivotStyles = {
linkIsSelected: {
selectors: {
':before': {
height: '6px', // was previously defaulted at 2px
}
}
}
};
I scoured about 4.24 million sites to find this answer but here are some of the most helpful in case they are of interest:
the actual source code of Pivot.styles.ts
the Microsoft Fluent UI Pivot Documentation
A deep examination of the classes using Chrome Dev Tools also helped. ;)
Here's a picture of the end result:

There is issue $scope value doesn't bind to ng-model at first time load

I want to display some default template in text area at very first loading.I tried to get that output but my code doesn't work.I put all breakpoints that value change.that also doesn't work. When second time data will display on text area.
<textarea meditor style="height: 100%;margin-top: 10px;border: 1px solid lightgray;" ng-model="activity.GoCode" name="gocode" required>{{activity.GoCode}}</textarea>
function getGoCode(data) {
if (data.GoCode == undefined) {
$scope.activity.GoCode = "test";
} else {
$scope.activity.GoCode = data.GoCode;
};
}
I Found solution for this
I set a value initially for this variable (activity.GoCode) before that I create
'$scope.activity' object and then assign value for when data is loaded.
$scope.ReadSampleGoFile = function () {
$http.get('settings/sampleGO.txt').then(function (data) {
//use the file data here
// console.log(data.data);
$scope.activity=[];
$scope.activity.GoCode=data.data;
});
}
When does your function getGoCode() is called, upon page load ?
I see one issue with your code even if it is not probably the problem here, having:
ng-model="activity.GoCode"
and
{{activity.GoCode}}
is redundant as explain here and will cause an exception in Internet explorer (Error: Invalid Argument) as explain here
Also what is the meditor directive doing, it is possible that this directive is the source of your problem as I don't see why your code would not work

JQuery Steps: how to disable a step?

I am using JQuery Steps, which has been excellent. But some things are proving tricky.
Suppose I am on step 2. Depending on what is selected in my form, I might want to skip directly to step 4 when "next" is clicked, and further, disable the step 3 anchor altogether. The step 3 anchor should still appear in the steps list, it should just be grayed out and disabled.
I cannot seem to get this to work. Any advice from the JQuery Steps gurus?
I have tried several variants of the following in onStepChanged:
if ($('#option').val() == 'foo') {
$('#wizard-t-3').attr('disabled', true);
$('#wizard-t-3').addClass('disabled');
} else {
$('#wizard-t-3').attr('disabled', false);
$('#wizard-t-3').removeClass('disabled');
}
if (currentIndex == 2) {
$(this).steps('next');
}
And applied what I thought was appropriate CSS:
.wizard>.steps a .disabled,.wizard>.steps a:hover .disabled,.wizard>.steps a:active .disabled,.wizard>.steps a:visited .disabled {
color: #777;
cursor: default
}
But it doesn't seem to do the trick. And it's not just the CSS that doesn't seem to work, the step still seems to be enabled. Everything looks right in the Chrome debugger, but it's not working right. I am obviously confused.
I realize this is sort of an old question, but here's what I did to remedy this.
Add the following to jquery.steps.js:
$.fn.steps.incomplete = function (i) {
var wizard = this,
options = getOptions(this),
state = getState(this);
if (i < state.stepCount) {
var stepAnchor = getStepAnchor(wizard, i);
stepAnchor.parent().addClass("disabled");
stepAnchor.parent().removeClass("done")._enableAria(false);
refreshSteps(wizard, options, state, i);
}
};
...and then call it by using this:
$("#yourWizard").steps('incomplete', stepNumber);
Hope this helps.

Filtering spcific with checkboxes

My question is that I want a filtering system that will filter by checked checkboxes.
The tool is to my comparison website where I compare TV packages.
my visitors should filter the packages by the tv-channels they want to se.
example;
Checkbox 1: Discovery
Checkbox 2: Animal PLanet
Checkbox 3: Disney Channel
Output should be the matching TV-package
Package 1: (contains Discovery and Disney channel)
Package 2: (contains Animal Planet, Disney channel)
Package 3: (contains Animal Planet)
So if checkbox 1 is checked it should only show package 1.
if checkbox 1 + checkbox 2 is checked it should say "No match found, but this package was was closest to your choice"
if checkbox 2 + checkbox 3 is checked it should only show package 2 which match the visitors choice exactly.
I hope your can help me out. I have been searching a lot after this specific solution without any success.
I think it should be in Jquery. i have seen some simular filtering examples, but no one there are like my wish above.
This is an old question, but... I'll take a shot. Working jsfiddle: http://jsfiddle.net/a0nnrfua/
I think a lot really depends on how you intend to define "closest", but presuming a jQuery solution and hopefully your browser requirements aren't TOO far in the past, you could use the data- attributes and jQuery to come up with some relatively simple functions. Or even use the value portions of the checkboxes really.
Psuedocode, it would look like:
Define a click or change handler to detect whenever a checkbox has been touched/changed.
Define a function that will scan all checked items and pass the values into your "closest package" function.
Based on the results of that function, filter your package selection so that your choices are highlighted or marked in some way.
So let's presume the following HTML markup:
<h3>TV Channels</h3>
<div id="TVChannelSelections">
<input type="checkbox" class="tvchannel" name="tvchannel" id="tvchannel_Discovery" value="Discovery" />
<label for="tvchannel_Discovery">Discovery Channel</label>
<br/>
<input type="checkbox" class="tvchannel" name="tvchannel" id="tvchannel_AnimalPlanet" value="Animal Planet" />
<label for="tvchannel_AnimalPlanet">Animal Planet</label>
<br/>
<input type="checkbox" class="tvchannel" name="tvchannel" id="tvchannel_DisneyChannel" value="Disney Channel" />
<label for="tvchannel_Disney">Disney Channel</label>
<br/>
</div>
<div id="message"></div>
<h3>Packages</h3>
<div id="FilteredPackages">
<div class="package deselected" id="Package1" data-channels="Discovery,Disney Channel">Package #1</div>
<div class="package deselected" id="Package2" data-channels="Animal Planet,Disney Channel">Package #2</div>
<div class="package deselected" id="Package3" data-channels="Animal Planet">Package #3</div>
</div>
So in jQuery, your generic change or click handler would be defined in code: Note that I'm saying, any element on your page that has the class "tvchannel" defined, if there's ever a change that occurs, run my filter function.
<script type="text/javascript" src="../path/to/jQuery/library"></script>
<script type="text/javascript">
$(document).ready(function() {
$(".tvchannel").on("change", function() {
FilterMySelectedChannels();
});
});
</script>
Now we can define your Filter function. We're going to assume two things. #1, that we want to find all the selected checkboxes and their values. Then we're going to iterate through the data-channels property of all of our packages (defined as elements with class = "package"). We'll use some form of string comparison and boolean logic to define what a complete match is vs. a close but no cigar match vs. a complete fail.
In order to keep track of things I'm using 3 classes, selected, deselected, and close.
In css, you can decide whether you want notselected to mean "hide the package completely" (i.e. display: none;) or maybe you want it to be visible but greyed out and "struck out" (i.e. text-decoration: strikethrough; color: grey;}
I'm going to use kind of a brute force way of doing the comparison. There are better array functions and comparison functions in javascript, but this should be relatively clear for most people and I trust the good folks at stackoverflow to chime in with better solutions. But this should get you started. :)
<script type="text/javascript">
function FilterMySelectedChannels() {
$checkedboxes = $(".tvchannel:checked");
$packages = $(".package");
var bAnyFound = false;
$packages.each(function () {
var bCloseButNoCigar = false;
var bCompleteMatch = true;
var packagearray = $(this).data("channels").split(",");
var $currentPackage = $(this);
$checkedboxes.each(function () {
if ($.inArray($(this).val(), packagearray) != -1) {
bCloseButNoCigar = true;
} else {
bCompleteMatch = false;
}
});
if (bCompleteMatch) {
$currentPackage.removeClass("selected").removeClass("deselected").removeClass("close").addClass("selected");
bAnyFound = true;
} else if (bCloseButNoCigar) {
$currentPackage.removeClass("selected").removeClass("deselected").removeClass("close").addClass("close");
} else {
$currentPackage.removeClass("selected").removeClass("deselected").removeClass("close").addClass("deselected");
}
});
if (bAnyFound) {
$("#message").html("The following matches were found");
} else {
$("#message").html("No actual matches were found, but here are some close matches based on your selections");
$(".package.close").removeClass("deselected").removeClass("close").removeClass("selected").addClass("selected");
}
}
</script>
<style type="text/css">
.selected {
color: red;
background-color: yellow !important;
}
.deselected {
color: grey;
text-decoration: strike-through !important;
background-color: white !important;
}
</style>
There are obvious optimizations that could probably work here, but it's a start for those trying to do something similar. Note that it assumes that your markup is dynamically generated or properly coded. If you need to guard against human typos, converting your text using .toLowerCase/UpperCase and using the .Trim functions to eliminate extra space will assist. But you still have to choose your data values wisely so there's no overlap. And if you choose them well enough you can use better techniques such as regular expressions and wildcard searches to make the code a bit shorter.
Hope this helps someone!

Resources